PAT甲级刷题实录——1014

原题链接

https://pintia.cn/problem-sets/994805342720868352/problems/994805498207911936

思路

这题需要用到队列,而且不止一条。首先是每个等待窗口各需要一条,另外在黄线外的等待顾客需要一条。C++提供了现成了现成的队列类型,只要引用头文件queue即可。

算法基本运行过程是:在输入顾客等待时间时依次填满每条队列,超出队列容量的,即编号大于N*M+1的顾客,则push进黄线外的等待队列中。当有窗口有顾客处理完毕后,则将该顾客pop出队列,同时从黄线外等待队列中pop出一个顾客并push进该窗口对应的队列中。最后直到黄线外等待队列为空为止。当然,光靠这个基本运行过程去写代码肯定是不行的,因为还需要在算法运行过程中计算每个顾客处理完成的时间,同时还需要判断何时进出队列。这也是本道题最难的两个地方。因为C++没有提供处理时间相关的类型,所以直接用时刻来计算非常麻烦,我们可以转变一种思路,即将时间转换为从8点开始经过的分钟数,最后输出结果的时候转换为HH:MM形式的时刻即可,这样就大大方便了计算。对于解决两个难点问题,我的思路是:记录每个队列队首元素处理完毕后的时间(从8点开始的经过的分钟数)hTime,这个时间也就是黄线外等待队列中的元素能够进入该队列的时间。另外记录每个队列队尾元素处理完毕后的时间(从8点开始的经过的分钟数)tTime,这个时间用于计算新进入队列的顾客开始处理的时间以及最终处理完毕的时间。在调度时,每次按队列下标依次找hTime最小的队列,将该队列中的队首元素弹出,在队尾插入从黄线外等待队列弹出的元素,同时更新hTime和tTime,并记录新进入队列顾客的开始时间和结束时间。这种方法能够符合题目如果两个队列都有一样多的空位,找编号更小的队列,假设出现多条队列的队首元素同时处理完毕,即多条队列都有空位,因为我们是按下标从小到大找的,只要把判断条件改为小于就能够找到编号最小的队列,而且下一次循环还是能找到编号最小的。对于题目另一个要求,即找最短的队列插入,这个情况实际上只会出现在最初初始化的阶段,初始化之后的运行过程中一旦一条队列有空位马上就会有顾客填入,即所有队列基本都是处于满员状态,最多出现一个空位,不存在多个空位的队列,因此无需考虑长短问题。

另外我在做这题的时候一开始有一个理解有误的地方,我以为只要顾客的结束时间晚于17:00那就不能处理了,但实际上是开始时间不能晚于17:00,如果在17:00点前开始了,哪怕最后结束的时间超过了17:00,也得给他处理完毕。这也是真实工作生活中的情况。

代码

#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct cInfo
{
    int cNum;   //顾客编号
    int pTime;  //需要处理的时长
    int beginTime, endTime; //开始处理的时间,结束处理的时间
};
int main()
{
    int N, M, K, Q;
    cin >> N >> M >> K >> Q;
    vector<queue<cInfo> > lines(N+1);
    queue<cInfo> outWait;   //在黄线外等待的顾客
    vector<int> hTime(N + 1, 0);    //队列队首处理完毕后的时间(自8点算起)
    vector<int> tTime(N + 1, 0);    //队列队尾处理完毕后的时间
    vector<cInfo> customers(K+1);   //每位顾客处理完毕后的时间
    for (int i = 0; i < K; i++)
    {
        int pTime, lNum;
        lNum = (i % N) + 1; //计算队列号
        cin >> pTime;
        cInfo customer;
        customer.cNum = i + 1;
        customer.pTime = pTime;
        if (i < M * N)  //队列还有空位
        {
            if (i < N)
                hTime[lNum] = pTime;    //每个队列添加第一个顾客,确定队首处理完毕后的时间
            lines[lNum].push(customer);
            customer.beginTime = tTime[lNum];   //开始时间为前一个队尾结束的时间
            //customers[i + 1].beginTime = tTime[lNum];
            tTime[lNum] += pTime;   //更新当前队尾
            customer.endTime = tTime[lNum];
            customers[i + 1] = customer;    //结束时间为更新后的队尾时间
        }
        else    //队列全部已满
        {
            outWait.push(customer);
        }
    }
    while (outWait.size() != 0) //在黄线外等待的不为空
    {
        int min=hTime[1], num = 1;
        for (int i = 2; i < N + 1; i++)
        {

            if (hTime[i] < min)
            {
                min = hTime[i];
                num = i;
            }
        }
        lines[num].pop();
        cInfo c = outWait.front();
        outWait.pop();
        lines[num].push(c);
        hTime[num] += lines[num].front().pTime;
        customers[c.cNum].beginTime = tTime[num];
        tTime[num] += c.pTime;
        customers[c.cNum].endTime = tTime[num];
    }
    for (int i = 0; i < Q; i++)
    {
        int cNum;
        cin >> cNum;
        if (customers[cNum].beginTime >= 540)
            cout << "Sorry" << endl;
        else
        {
            int hour, minute;
            hour = 8 + customers[cNum].endTime / 60;
            minute = customers[cNum].endTime % 60;
            if (hour < 10)
                cout << '0' << hour << ':';
            else
                cout << hour << ':';
            if (minute < 10)
                cout << '0' << minute << endl;
            else
                cout << minute << endl;
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/aopstudio/p/12236912.html

时间: 2024-08-30 17:02:09

PAT甲级刷题实录——1014的相关文章

PAT甲级刷题实录——1004

原题链接 https://pintia.cn/problem-sets/994805342720868352/problems/994805521431773184 思路 很明显这题需要用到树这个数据结构,问题是怎么来存.一开始我是这样想的:因为它只问了每一层叶子结点数,所以最简单的情况下我只需要两个数据就行,一个是结点的所在的层级,另一个是结点是否含有子结点.所有的结点都存储在vector中,另外创建一个存储每一层叶子结点个数的数组用于最后输出结果.遍历vector,记录每一层不含有子结点的个

PAT甲级刷题实录——1011

原题链接 https://pintia.cn/problem-sets/994805342720868352/problems/994805504927186944 思路 这题就很简单了,每行输入的时候找出最大的记录下来,同时记录下标.输入完毕后根据下标转换成结果(W,T,L)并储存起来,再根据每行的最大值计算profit.最后输出结果和profit即可,代码如下. 代码 #include <iostream> #include <vector> using namespace s

PAT甲级刷题实录——1010

原题链接 https://pintia.cn/problem-sets/994805342720868352/problems/994805507225665536 思路 这题是到目前为止比较难的一题,评测系统的通过率也只有 0.11. 首先需要理解基本题意.题目的要求是给一个已知进制的数,求能不能找出一个进制使得另一个未知进制的数在该进制下和已知进制的数数值相等.大部分人应该都会想到将两个数的数值都转换为十进制后做比较. 在理解了基本题意之后,做的过程中发现这题还有不少坑. 进制是没有上限的.

PAT甲级刷题实录——1013

原题链接 https://pintia.cn/problem-sets/994805342720868352/problems/994805500414115840 思路 题目大意是说一些城市之间有路相通,假设其中一个城市被敌方占领了,计算需要新修多少条路才能让剩下的城市全部联通.首先这是一个典型的图论问题,我们可以用邻接矩阵去存城市之间的联通关系.可以用深度遍历的思想去解决这个问题.思路大意如下:建立一个数组存储哪些城市已经被联通,1代表已联通,0代表未联通:定义一个变量记录需要新修的路的数量

1085. Perfect Sequence (25)-PAT甲级真题

1085. Perfect Sequence (25) 时间限制 300 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CAO, Peng Given a sequence of positive integers and another positive integer p. The sequence is said to be a "perfect sequence" if M <= m * p where M and m

1020. Tree Traversals (25) PAT甲级真题

之前我看了这道题,实在是看不懂网上的解题答案,他们的具体思路基本上就是通过后续遍历和中序遍历,直接推出层次遍历. 我苦思冥想了半天,是在没看懂这种思路,于是想了一个笨点的但是也比较好理解的思路,通过后续和中序,先推出整个二叉树,再考虑 对二叉树层次遍历. 本题还有一点要注意的时在输出结果的末尾,如果使用了类似 pirntf("%d ",data); 这样的格式是不对的,一定要对末尾进行判断消除最尾端的空格. 首先最核心的部分是通过两次遍历反推回二叉树:这里的思路是,后续遍历的最末尾,一

1078. Hashing (25)-PAT甲级真题

1078. Hashing (25)The task of this problem is simple: insert a sequence of distinct positive integers into a hash table, and output the positions of the input numbers. The hash function is defined to be "H(key) = key % TSize" where TSize is the

PAT甲题题解-1111. Online Map (30)-PAT甲级真题(模板题,两次Dijkstra,同时记下最短路径)

题意:给了图,以及s和t,让你求s到t花费的最短路程.最短时间,以及输出对应的路径.   对于最短路程,如果路程一样,输出时间最少的. 对于最短时间,如果时间一样,输出节点数最少的.   如果最短路程和最短时间路径一样,合并输出一次即可. 纯粹就是练习dijkstra,没什么难的. 第一次dijkstra求最短路程,记录下每个节点的路程和时间. 第二次dijkstra求最短时间,记录下每个节点的时间和经过的节点数. pre数组用来存储前驱节点,保存路径 #include <iostream>

C刷题记录-1014

题目描述 求Sn=1!+2!+3!+4!+5!+-+n!之值,其中n是一个数字(n不超过20). 输入 n 输出 Sn的值 样例输入 5 样例输出 153 1 #include <stdio.h> 2 3 unsigned long long int get_factorial (int n); 4 5 int main(){ 6 7 int n,i; 8 unsigned long long int SN = 0; 9 scanf("%d",&n); 10 11