pat甲级1014柳神代码解析自学复盘用

这道题我自己是很没头绪,感觉这种题没什么算法但却很难。

这里有点抽象类的感觉,

1,首先,对于输入的每个人的办事时间,开辟了time[]数组来存储每个人的时间,实际上每个人也只有他的时间信息和下标信息(下标代表他是第几个;

2,struct node {
    int poptime, endtime;
    queue<int> q;
};

int main() {
    int n, m, k, q, index = 1;
    scanf("%d%d%d%d", &n, &m, &k, &q);
    vector<int> time(k + 1), result(k + 1);
    for(int i = 1; i <= k; i++) {
        scanf("%d", &time[i]);
    }
    vector<node> window(n + 1);
    vector<bool> sorry(k + 1, false);

这段代码,实际上抽象出来了柜台这个类,一个柜台有一串排队的队列,有队列的队首和队尾的人办完事情出队的时间,队首的时间可以用来判断下一个即将入队的人是排哪个柜台,队尾的时间???,(我暂时想不出来队尾的时间有什么用,因为一个人挨个办完事,队首的时间叠加起来不就是队尾吗?)同时还创建了一个sorry[],存放最后没时间服务的人。result[index]代表每个人办完事的时间。

3.  for(int i = 1; i <= m; i++) { 
        for(int j = 1; j <= n; j++) {
            if(index <= k) {  //判断是否人都排完了
                window[j].q.push(time[index]);
                if(window[j].endtime >= 540)
                    sorry[index] = true;
                window[j].endtime += time[index];
                if(i == 1)
                    window[j].poptime = window[j].endtime;
                result[index] = window[j].endtime;
                index++;
    }}}

这段代码,总体上说是把当前的n个窗口先都排上一个人,然后n个窗口对应m个队列也给他排满,另外注意整篇代码都需要用index来记录当前这个人的下标。其实这里就体现出endtime的用处了,插入队的时候,如果最后一个人办完事的时候银行都下班了,那这些人都存入sorry数组中去,而不是排队去。  window[j].q.push(time[index]);(这里我对这行代码不是特别理解,q是一个队列,放入时间,差不多是这样存【22 50 70 510】)  if(i ==1 )  window[j].poptime = window[j].endtime;另外这行代码,第一个排进队列的人,那么这个队列的poptime和endtime都是第一个人的时间,(这里我也不理解,好像到这行代码的时候都没有初始化或对poptime与endtime赋值)。其实已经赋值了,上面那行代码执行前已经给endtime赋值了,这行代码就是给poptime赋值,而且也只需要对第一行的poptime赋值 。result[index] = window[j].endtime; index++;这行代码也想了一下,其实这里执行的for循环,每一个人都是一个个排进去的,那么当前这个人排进去前, 已经执行了window[j].endtime += time[index];那么当前这个人办完事的时间就是当前队列的endtime(因为当前排进去的人就是队尾的人)。window[j].q.push(time[index]);这行代码是为第四大步骤做准备工作,因为必须要把每个人的时间保存到队列中,才能在更新队列的时候获取到时间哇。

4到上面那段,算是完成了先把窗口人数排满的工作,接下来还有人没有排上队,就需要判断当前哪个队最先走人,然后去排这个队,并且更新这个队的poptime,endtime。那么怎么具体完成呢?


    while(index <= k) { //判断人排完没有,为什么用while,因为这里是一个个排人,
        int tempmin = window[1].poptime, tempwindow = 1;
        for(int i = 2; i <= n; i++) {
            if(window[i].poptime < tempmin) {
                tempwindow = i;
                tempmin = window[i].poptime;
            }
        }  //找出当前最快办完事的窗口,
        window[tempwindow].q.pop(); //当前办完事的窗口的人的时间弹出队列
        window[tempwindow].q.push(time[index]);//把人传入这个办完事的队列
        window[tempwindow].poptime +=  window[tempwindow].q.front();//更新当前这个队列的弹出时间,就是当前队列的第一个人的办事时长,注意是+=,
        if(window[tempwindow].endtime >= 540)//如果超过540
            sorry[index] = true;//加入sorry[]数组。注意上面第二段的时候就有一个加入sorry队列的操作,就是防止前面的几个人办事办太久的情况。
        window[tempwindow].endtime += time[index];//更新endtime
        result[index] = window[tempwindow].endtime;//排到队的人的办完事时间就是endtime,因为他是排在队尾的。
        index++;
    }

5.最后是输出

  for(int i = 1; i <= q; i++) {
        int query, minute;
        scanf("%d", &query);//query保存查询的人的下标
        minute = result[query];//查询的人办完事的时间
        if(sorry[query] == true) {//如果在sorry[]中
            printf("Sorry\n");
        } else {
            printf("%02d:%02d\n",(minute + 480) / 60, (minute + 480) % 60);
        }//小时是/60取整,分钟是%60取余。

6思路总结与启发

这类实际问题,要进行抽象化,如本题中,抽象出柜台和顾客。

选好数据结构,这道题首先肯定有queue,因为已经是个排队问题了,哪些数据在输入中要保存,就需要开辟空间,比如sorry,result。

柳神的思路就是这个核心:poptime是为了让黄线外的人可以计算出哪一个队列先空出人来(poptime最小的那个先有人服务完毕),endtime是为了入队后加上自己本身的服务所需时间可以计算出自己多久才能被服务完毕~且前一个人的endtime可以得知自己是不是需要被Sorry(如果前一个人服务结束时间超过17:00,自己当前入队的人就是sorry),还有一个queue表示所有当前该窗口的排队队列。

怎么想出来的呢?那就要根据题意,一步步来想,自然会想到这样做的原因。

而且柳神代码很清晰思路,先排满n个窗口m队,然后如果还有剩余在一个个排。中间还有很多细节问题。做此类题时读懂题意,慢慢想,顺着步骤一步步在草稿纸上写下步骤,遇到问题要想用什么样的数据结构与算法去解决,比如这个人应该被排到哪个队,那就自然想到要比较当前哪个队最先走人,就想到要有个poptime,也要想到这个人加到这个队伍中,他的结束时间是多少,他能不能被服务还能不能加到队伍中,要想到有endtime,以及如何更新这些数据。
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值