PAT甲题题解-1095. Cars on Campus(30)-(map+树状数组,或者模拟)

题意:给出n个车辆进出校园的记录,以及k个时间点,让你回答每个时间点校园内的车辆数,最后输出在校园内停留的总时间最长的车牌号和停留时间,如果不止一个,车牌号按字典序输出。

几个注意点:

1.如果一个车连续多次进入,只取最后一个

2.如果一个车连续多次出去,只取第一个

3.一个车可能出入校园内好几次,停留时间取总和

实际上题目就是让我们求某个时间段内的车辆总和,时间段其实就相当于一个区间,区间求和的话,很快就联想到树状数组和线段树。然而怎么将时间段和区间联系起来呢,那就存储出现在记录和询问里的所有时间点,从小到大排序,索引即为区间中节点的编号。

那么问题就转化为车辆停留的时间对应区间[l,r],该区间内每个时间点的车辆数都+1,询问就是求某个点的值。这里就采用了树状数组的“区间更新,单点查询”,不理解的可以参考下面链接:

http://www.cnblogs.com/chenxiwenruo/p/3430920.html

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string.h>
#include <string>
#include <map>
/*
有时候第五个样例会超时,限时220ms,运行的话208ms。
*/
using namespace std;

const int maxn=90000+5;
string timeline[maxn];  //存储出现的时间
string query[maxn];
int parking[maxn]; //树状数组,用于统计区间内的车辆数
int timecnt=0;
map<string,int>visnumber; //记录出现的车牌号
map<string,int>vistime; //记录出现的每个时间点
struct Record{
    string plate_number;
    string time;
    char status[5];
    bool operator<(const Record tmp)const{
        //不知道为啥,原本<=0就会导致在第3、6个样例段中对record的sort排序产生段错误
        if(time.compare(tmp.time)<0)
            return true;
        else
            return false;
    }
}record[maxn];

struct Car{
    string plate_number;
    //char time[10];
    int l,r;
    int timelength=0;
    bool operator<(const Car tmp)const{
        if(timelength==tmp.timelength){
            if(plate_number.compare(tmp.plate_number)<=0)
                return true;
            else
                return false;
        }
        else
            return timelength>tmp.timelength;
    }
}car[maxn];

/*
二分查找某个时间点对应的索引
*/
int binarySearch(string str){
    int l=1,r=timecnt,mid;
    while(r>=l){
        mid=(l+r)>>1;
        if(str.compare(timeline[mid])==0)
            return mid;
        if(str.compare(timeline[mid])<0){
            r=mid-1;
        }
        else{
            l=mid+1;
        }
    }
    return -1;
}
//以下三个函数为树状数组的操作
int lowbit(int x){
    return x&(-x);
}
/*
区间更新
*/
void update(int x,int val){
    while(x){
        parking[x]+=val;
        x-=lowbit(x);
    }
}
/*
单点求和
*/
int sum(int x){
    int sum=0;
    while(x<maxn){
        sum+=parking[x];
        x+=lowbit(x);
    }
    return sum;
}

int main()
{
    int n,k;
    scanf("%d %d",&n,&k);
    for(int i=1;i<=n;i++){
        cin>>record[i].plate_number>>record[i].time;
        scanf("%s",record[i].status);
    }

    sort(record+1,record+n+1);

    timecnt=0;
    timeline[++timecnt]="00:00:00";
    vistime["00:00:00"]=1;

    timeline[++timecnt]="23:59:59";
    vistime["23:59:59"]=1;

    for(int i=1;i<=n;i++){
        if(!vistime[record[i].time]){
            vistime[record[i].time]=1;
            timeline[++timecnt]=record[i].time;
        }
    }

    for(int i=0;i<k;i++){
        cin>>query[i];
        if(!vistime[query[i]]){
            timeline[++timecnt]=query[i];
            vistime[query[i]]=1;
        }
    }
    sort(timeline+1,timeline+timecnt+1);
    int carcnt=0;
    /*
    求每辆车进出校园对应的时间区间段,以及停留的时间
    同一辆车连续多次进入,取最后一个。
    同一辆车连续多次出去,取第一个。
    */
    for(int i=1;i<=n;i++){
        if(record[i].status[0]==‘i‘){
            visnumber[record[i].plate_number]=i;
        }
        else if(visnumber[record[i].plate_number]!=0 && record[i].status[0]==‘o‘){
            int in=visnumber[record[i].plate_number];
            car[carcnt].plate_number=record[i].plate_number;
            car[carcnt].l=binarySearch(record[in].time);
            car[carcnt].r=binarySearch(record[i].time);

            int hh1,mm1,ss1;
            int hh2,mm2,ss2;
            hh2=(record[i].time[0]-‘0‘)*10+(record[i].time[1]-‘0‘);
            mm2=(record[i].time[3]-‘0‘)*10+(record[i].time[4]-‘0‘);
            ss2=(record[i].time[6]-‘0‘)*10+(record[i].time[7]-‘0‘);

            hh1=(record[in].time[0]-‘0‘)*10+(record[in].time[1]-‘0‘);
            mm1=(record[in].time[3]-‘0‘)*10+(record[in].time[4]-‘0‘);
            ss1=(record[in].time[6]-‘0‘)*10+(record[in].time[7]-‘0‘);
            car[carcnt].timelength=(hh2*3600+mm2*60+ss2)-(hh1*3600+mm1*60+ss1);
            visnumber[record[i].plate_number]=0; //这样做是可能后来该车又进来
            carcnt++;
        }
    }

    memset(parking,0,sizeof(parking));
    /*
    如果某辆车l进、r出,则对应区间段[l,r-1]的车辆数+1
    */
    for(int i=0;i<carcnt;i++){
        int l=car[i].l;
        int r=car[i].r;
        //更新[l,r-1),该区间内每个时间段数量都+1。相当于[1,r-1]的先+1,[1,l-1]的再-1
        update(r-1,1);
        update(l-1,-1);
    }
    //求对应某个时间点,校园的车辆数
    for(int i=0;i<k;i++){
        int idx=binarySearch(query[i]);
        printf("%d\n",sum(idx));
    }
    visnumber.clear();
    //有可能一辆车进出校园好几次,所以要将停留的时间累加
    for(int i=0;i<carcnt;i++){
        if(!visnumber[car[i].plate_number]){
            visnumber[car[i].plate_number]=i;
        }
        else{
            int idx=visnumber[car[i].plate_number];
            car[idx].timelength+=car[i].timelength;
        }
    }
    sort(car,car+carcnt);
    cout<<car[0].plate_number;
    for(int i=1;i<carcnt;i++){
        if(car[i].timelength==car[0].timelength){
            cout<<" "<<car[i].plate_number;
        }
    }
    int h=car[0].timelength/3600;
    int m=(car[0].timelength%3600)/60;
    int s=car[0].timelength%60;
    printf(" %02d:%02d:%02d\n",h,m,s);
    return 0;
}

(其实这题也可以纯按模拟题来做,用树状数组有点大材小用了,按照记录的时间排序,模拟车辆的进出,用一个变量记录校园当前的车辆数量。)

时间: 2024-12-29 04:26:46

PAT甲题题解-1095. Cars on Campus(30)-(map+树状数组,或者模拟)的相关文章

PAT甲题题解-1119. Pre- and Post-order Traversals (30)-(根据前序、后序求中序)

(先说一句,题目还不错,很值得动手思考并且去实现.) 题意:根据前序遍历和后序遍历建树,输出中序遍历序列,序列可能不唯一,输出其中一个即可. 已知前序遍历和后序遍历序列,是无法确定一棵二叉树的,原因在于如果只有一棵子树可能是左孩子也有可能是右孩子.由于只要输出其中一个方案,所以假定为左孩子即可.下面就是如何根据前序和后序划分出根节点和左右孩子,这里需要定义前序和后序的区间范围,分别为[preL,preR],[postL,postR]. 一开始区间都为[1,n],可以发现前序的第一个和后序的最后一

PAT甲题题解-1112. Stucked Keyboard (20)-(map应用)

题意:给定一个k,键盘里有些键盘卡住了,按一次会打出k次,要求找出可能的坏键,按发现的顺序输出,并且输出正确的字符串顺序. map<char,int>用来标记一个键是否为坏键,一开始的时候都为0,表明所有的键为坏键. 然后遍历每个字符,统计当前字符连续出现的次数cnt,则只要存在cnt%k!=0,则表明为好键,另其map=1. 最后再for一遍字符串,存储坏键字符串和正确字符串,最后输出即可. #include <iostream> #include <cstdio>

PAT甲题题解-1014. Waiting in Line (30)-模拟,优先级队列

题意:n个窗口,每个窗口可以排m人.有k为顾客需要办理业务,给出了每个客户的办理业务时间.银行在8点开始服务,如果窗口都排满了,客户就得在黄线外等候.如果有一个窗口用户服务结束,黄线外的客户就进来一个.如果有多个可选,选窗口id最小的.输出查询客户的服务结束时间. 如果客户在17点(注意是包括的!!!就在这里被坑了,一开始还以为不包括...)或者以后还没开始服务,就输出Sorry如果已经开始了,无论多长都会继续服务的. 思路:建立一个优先级队列,存储在黄线之内的所有客户.对于m*n之前的人,依此

PAT 1095. Cars on Campus (30)

1095. Cars on Campus (30) Zhejiang University has 6 campuses and a lot of gates. From each gate we can collect the in/out times and the plate numbers of the cars crossing the gate. Now with all the information available, you are supposed to tell, at

1095. Cars on Campus (30)——PAT (Advanced Level) Practise

题目信息 1095. Cars on Campus (30) 时间限制220 ms 内存限制65536 kB 代码长度限制16000 B Zhejiang University has 6 campuses and a lot of gates. From each gate we can collect the in/out times and the plate numbers of the cars crossing the gate. Now with all the informati

BZOJ 3038 上帝造题的七分钟2 (并查集+树状数组)

题解:同 BZOJ 3211 花神游历各国,需要注意的是需要开long long,还有左右节点需要注意一下. #include <cstdio> #include <cmath> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; LL a[100005],c[100005]; int f[100005],n,m,op,l,r,t; int s

【题解】Leyni,罗莉和队列(树状数组)

[题解]Leyni,罗莉和队列(树状数组) HRBUST - 1356 将整个序列reverse一下,现在就变成了从高到低的排队.题目就变成了,定位一个妹子,问这个妹子前面的比这个妹子小的妹子中,下标最小的数是哪个. 所以以年龄为下标,以(原数组)下标为值建立一颗值域线段树,由于我们只要找到在原数组下标最小的哪一个,所以相当于动态维护前缀\(\min\) ,由于我们只要查询比当前定位妹子小的一段前缀,所以拿树状数组代替就是了. 由于这群妹子是神仙(年龄可达1e9),所以要离散化一下. 复杂度\(

PAT甲题题解-1101. Quick Sort (25)-大水题

快速排序有一个特点,就是在排序过程中,我们会从序列找一个pivot,它前面的都小于它,它后面的都大于它.题目给你n个数的序列,让你找出适合这个序列的pivot有多少个并且输出来. 大水题,正循环和倒着循环一次,统计出代码中的minnum和maxnum即可,注意最后一定要输出'\n',不然第三个测试会显示PE,格式错误. #include <iostream> #include <cstdio> #include <algorithm> #include <map&

PAT甲题题解-1108. Finding Average (20)-字符串处理

求给出数的平均数,当然有些是不符合格式的,要输出该数不是合法的. 这里我写了函数来判断是否符合题目要求的数字,有点麻烦. #include <iostream> #include <cstdio> #include <algorithm> #include <string.h> using namespace std; const int maxn=105; bool islegal(char*str){ int len=strlen(str); int p