A1014
个人思路
个人思路与答案略有不同,要明确每个客户的结束时间等于当前窗口的结束时间+服务时间。因此窗口结束时间windowEnd[]为了计算每个客户的结束时间;窗口队首客户的结束时间windowFront[]为了在队满时寻找最早结束的窗口进行入队
- 首先初始化每一个窗口第一批客户,更新窗口的结束时间和队首客户的结束时间
- 随后对其余客户遍历
- 先遍历窗口,队满时记录最早结束窗口的id,队不满时记录人最少的窗口id,同时要注意编号最小
- 队不满时,入队,更新数据
- 对满时,出队,入队,更新数据
个人思路代码
采用deque容器,便于遍历和队首队尾插入删除
不知道哪里有问题,个人思路的代码有一个测试点未通过
#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3fffffff;
int N, M, K, Q;
struct Customer{
int endTime;
}customers[1005];
//处理时间,窗口结束时间,队首客户的结束时间
int serveTime[1005], windowEnd[25], windowFront[25];
deque<Customer>windowLine[25];//窗口队伍
int open = 8 * 60, close = 17 * 60;
//结束时间m,服务时间d
void display(int m, int d)//输出结束时间
{
if(m - d < close)
{
int hh = m / 60;
int mm = m % 60;
printf("%02d:%02d\n", hh, mm);
}
else//结束时间超过17:00输出sorry
printf("Sorry\n");
}
int main(int argc, char *argv[]) {
scanf("%d%d%d%d", &N, &M, &K, &Q);
//下标全从1开始
for(int i = 1; i <= K; ++i)
{
scanf("%d", &serveTime[i]);
}
//初始化每个窗口的第一批N个客户
for(int i = 1; i <= N; ++i)
{
windowEnd[i] = open + serveTime[i];
customers[i].endTime = windowEnd[i];
windowLine[i].push_back(customers[i]);
windowFront[i] = customers[i].endTime;
}
//从N + 1到 K客户
for(int i = N + 1; i <= K; ++i)
{
//遍历窗口,找出人数最少的窗口winID,临时记录所有队满中结束最早的endTimeID
int winID = -1, minLine = inf, minEndTime = inf, endTimeID = -1;
for(int j = 1; j <= N; ++j)
{
if(windowLine[j].size() == M)//当前队满
{
if(windowFront[j] < minEndTime)//找结束最早的窗口
{
minEndTime = windowFront[j];
endTimeID = j;
}
continue;
}
if(windowLine[j].size() < minLine)//找人最少队伍,且序号最小的
{
minLine = windowLine[j].size();
winID = j;
}
}
if(winID != -1)//队不满
{
customers[i].endTime = windowEnd[winID] + serveTime[i];
windowLine[winID].push_back(customers[i]);//排队
windowEnd[winID] += serveTime[i];
}
else//说明所有队满,找最早结束的窗口 ,考虑出队
{
//入队,一直是队满状态
winID = endTimeID;
windowLine[winID].pop_front();//队首出队
windowEnd[winID] = windowFront[winID] = windowLine[winID][0].endTime;//更新
customers[i].endTime = windowEnd[winID] + serveTime[i];
windowLine[winID].push_back(customers[i]);
}
}
for(int i = 1; i <= Q; ++i)
{
int query;
scanf("%d", &query);
display(customers[query].endTime, serveTime[query]);
}
return 0;
}
本题思路
- 先将队伍循环填满,此方法可保证每次找到人数最少,编号最小的队伍——很值得借鉴
- 17:00前开始服务,17:00后服务结束,也是有效的数据
- 对窗口采用结构体还是比较明智的,没有个人思路中数据结构太多显得有点乱
AC代码
#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3fffffff;
const int maxn = 1005;
int N, M, K, Q;
int serveTime[maxn], customer[maxn];//处理时间,客户结束时间
int open = 8 * 60, close = 17 * 60;
struct Window{
int frontTime = open;//队首客户结束时间
int endTime = open;//窗口结束时间
queue<int> q;
}windows[25];//初始化,由于是结构体数组,需要初始化
//结束时间m,服务时间d
void display(int m, int d)//输出结束时间
{
if(m - d < close)
{
int hh = m / 60;
int mm = m % 60;
printf("%02d:%02d\n", hh, mm);
}
else//结束时间超过17:00输出sorry
printf("Sorry\n");
}
int main(int argc, char *argv[]) {
scanf("%d%d%d%d", &N, &M, &K, &Q);
//下标全从0开始
for(int i = 0; i < K; ++i)
{
scanf("%d", &serveTime[i]);
}
int index = 0;//当前为入队的客户下标
//由于取模,窗口下标从0开始
for(int i = 0; i < min(N * M, K); ++i)//先把所有队列填满,如果K较小,会出现队列不满的情况
{
int winID = index % N;
windows[winID].q.push(index);//客户入队
windows[winID].endTime += serveTime[index];
if(index < N)//判断队首客户
windows[winID].frontTime = windows[winID].endTime;
customer[index] = windows[winID].endTime;
++index;
}
//所有队满,仍有尚未入队的客户
for(; index < K; ++index)
{
int winID = -1, minFrontTime = inf;
for(int i = 0; i < N; ++i)//找结束最早的队伍
{
if(windows[i].frontTime < minFrontTime)
{
winID = i;
minFrontTime = windows[i].frontTime;
}
}
windows[winID].q.pop();//队首出队
int cusID = windows[winID].q.front();
windows[winID].frontTime = customer[cusID];//更新队首结束时间
windows[winID].q.push(index);//入队
windows[winID].endTime += serveTime[index];//更新窗口结束时间
customer[index] = windows[winID].endTime;//
}
for(int i = 1; i <= Q; ++i)
{
int query;
scanf("%d", &query);
display(customer[query - 1], serveTime[query - 1]);//下标从0开始
}
return 0;
}