题目

解题思路

本题是一个经典的银行窗口排队问题,还是很有实际意义的。首先读完题,发现题目要求我们给出每个客户的业务结束时间,那么首先来处理存储结构和存储内容:

  • 银行的窗口肯定需要一个数组,我们用vector,原因是这个窗口包含的信息很多,我们需要创建一个结构体,包含start_time和end_time,以及一个队列q。两个时间变量表示当前银行窗口的开始服务时间和结束服务时间。队列q存放当前窗口服务客户的编号,比如说1号窗口的q有1,2,4号客户;2号窗口的q有3,5,6号客户。这三个东西是一个窗口所要包含的。
  • 然后每个客户有一个办业务的持续时间和结束时间,同时结束时间也是题目要求输出的。我们使用time数组来存放每个客户的持续时间,用time_out数组来存放这个客户几点结束业务。这个time_out也就是最后用来输出题目要求的数组,所以每个过程处理的信息最终都要汇总到这个数组。
  • 然后是一个sorry数组,如果有客户的时间时间超出了银行的关门时间,比如1号窗口17:30才能结束,那么后面的人就不能去1号窗口办理业务了,即输出sorry
  • 最后老规矩,时间嘛,我们都换算成整数,这个地方没有秒,那就少一个秒,单位是分钟就可以了

那么处理完后,我们来确定代码的一个思路:

  • 首先用time数组存储每个客户的业务需求时间
  • 然后我们处理黄线内的客户,黄线内的客户是按轮询处理,没有优先级,不会说哪个队伍少就去哪个队,是按顺序来的,这是一个坑点,题目的原话是( Each customer will choose the shortest line to wait in when crossing the yellow line. ),也就是说你在黄线外的人才需要考虑队伍的长短,黄线内的人是不需要考虑的,按轮询来就行,比如:2个窗口,黄线内容纳2人/每个窗口,那么前1,2,3,4号客户就按轮询的方式排队即可。4号以后的客户在按照队伍长短选择队伍。
  • 黄线后的客户,我们需要知道当前哪个队伍最短,那怎么表示队伍最短呢?其实每个窗口的start_time就是用来表示队伍最短的,因为start_time越小,那么说明这个窗口可以越早的办理业务,不就是等同于队伍最短么,同时还解决了权重的问题,即如果两个窗口的start_time一样,我们需要选窗口编号小的那个。
  • 最后通过sorry数组和time_out数组输出结果
说一下,这个题输出sorry后如果有空行是可以的,这个题不会判错,当然这样不好。

实现代码

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
#include<sstream>
#include<algorithm>
using namespace std;

typedef struct Node
{
    //这里要理解哦,这两个时间的具体作用,前者是为了确定当前窗口啥时候可以服务下一个客户
    //后者是计算当前客户的服务结束后的时间,其实就是题目要求输出的客户结束业务的时间
    int start_time=480,end_time=480;

    //这个队列其实能存放的客户编号永远不能超过M,只能<=M
    //因为黄线内一个队伍只能由M人,而q表示当前窗口黄线内的人的编号
    //如果一个窗口的黄线内有2人,那么第三个人想要来这个窗口办理业务,那么第一个人一定要先出队,第三个才能入队
    queue<int> q;
}NODE;

int main()
{
#ifdef ONLINE_JUDGE                
#else    
    freopen("1.txt", "r", stdin);
#endif 

    int N,M,K,Q;
    cin>>N>>M>>K>>Q;

    //这个地方要写k+1,因为人物编号从1开始,没有0号的
    vector<int> time(K+1),time_out(K+1);
    vector<bool> sorry(K+1,false);

    //人物编号从1开始,而循环变量作为数组下标,所以i=1开始
    for(int i=1;i<=K;i++)
    {
        cin>>time[i];
    }

    //这是窗口数组
    vector<NODE> window(K+1);
    int index=1;            //index是指当前人物编号,从1号开始处理

    //这个双循环表示处理黄线内的人,只要轮询即可
    //外循环是黄线人数/队
    for(int i=1;i<=M;i++)
    {
        //内循环是窗口
        for(int j=1;j<=N;++j)
        {   
            //先判断index时候超过了给定的K
            if(index<=K)
            {
                //当前客户入队,即挑选窗口,窗口的队列q登记信息
                window[j].q.push(index);

                //确保当前窗口没有下班,只要在17:00分以前空闲,都可以服务客户,即便当前窗口的end_time=16:59分
                //否则就是sorry,窗口下班
                if(window[j].end_time>=1020)
                    sorry[index]=true;

                //窗口更新end_time,即当前客户结束的时间
                window[j].end_time += time[index];

                //更新窗口的start_time,方便黄线后面的人找较短的队伍,以便排队
                //这个地方为啥不能是+=time[index],看着好像也对啊
                window[j].start_time += window[j].end_time;       

                //为啥不能用window[j].starttime+time[index]
                time_out[index]=window[j].end_time;                 

                index++;
            }
        }
    }

    //处理黄线外的,首先就是要找出队伍短的那个
    while(index <= K)
    {
        //用两个变量记录第一个窗口的start_time和窗口编号
        //这样的好处是可以找到最短的队伍,同时如果用多个相同长度的队伍,优先最小的窗口号
        int temp_start_time_min=window[1].start_time,temp_min_window_num=1;
        for(int j=2;j<=N;++j)
        {
            if(temp_start_time_min > window[j].start_time)
            {
                temp_start_time_min=window[j].start_time;
                temp_min_window_num=j;
            }
        }

        //找到了最短队伍所在的窗口,把窗口的队列的队首弹出,让后一个人进来
        window[temp_min_window_num].q.pop();
        window[temp_min_window_num].q.push(index);

        if(window[temp_min_window_num].end_time >= 1020)
            sorry[index]=true;
        else
        {   
            //依次更新end_time,start_time,以及time_out
            //比较上面的start_time更新,这里就是用temp数组进行更新,为啥这里可以,上面不行呢
            window[temp_min_window_num].end_time += time[index];
            window[temp_min_window_num].start_time += time[window[temp_min_window_num].q.front()];
            time_out[index] = window[temp_min_window_num].end_time;
        }
        //time_out[index]=window[temp_min_window_num].end_time;

        index++;
    }

    for(int i=1;i<=Q;++i)
    {
        int temp;
        cin>>temp;
        if(sorry[temp]==true)
            cout<<"Sorry"<<endl;
        else
        {
            printf("%02d:%02d\n",time_out[temp]/60,time_out[temp]%60);
        }
    }

    return 0;
}

lionの金库