3321.ATM队列
N 个人(编号 1∼N),排成一队在 ATM 机前准备取钱。
初始时,队列按编号升序的顺序排列。
第 i 个人要取 Ai 元钱。
一个人一次最多可以取 X 元钱。
当轮到某个人取钱时,如果其需要的钱的数量大于 X,则只能先取 X 元钱,然后去队尾重新排队,等待下次轮到他取钱时,继续去取。
当一个人取够钱时,他就会拿着钱离开队列。
现在,请你确定所有人离开队列的顺序。
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据第一行包含两个整数 N 和 X。
第二行包含 N 个整数 Ai。
输出格式
每组数据输出一个结果,每个结果占一行。
结果表示为 Case #x: y,其中 x 为组别编号(从 1 开始),y 是空格隔开的 N 个整数,表示所有人离开队列的顺序列表。
数据范围
60% 数据满足,1≤T≤100,1≤N≤100,
另外 40% 数据满足,1≤T≤10,1≤N≤10^5,
100% 数据满足,1≤Ai≤10^9,1≤X≤10^9。
输入样例:
2
3 3
2 7 4
5 6
9 10 4 7 2
输出样例:
Case #1: 1 3 2
Case #2: 3 5 1 2 4
样例解释
对于测试数据 1,共 3 个人,单轮最大取钱数量为 3。
取钱过程如下:
1.初始时,队列为 [1,2,3],第一个人需要 2 元钱,取完离队。
2.此时,队列为 [2,3],第二个人需要 7 元钱,所以先取 3 元,然后去队尾继续排队。
3.此时,队列为 [3,2],第三个人需要 4 元钱,所以先取 3 元,然后去队尾继续排队。
4.此时,队列为 [2,3],第二个人需要 4 元钱,所以先取 3 元,然后去队尾继续排队。
5.此时,队列为 [3,2],第三个人需要 1 元钱,取完离队。
6.此时,队列为 [2],第二个人需要 1 元钱,取完离队。
队列为空。
离队顺序为 [1,3,2]。
对于测试数据 2,共 5 个人,单轮最大取钱数量为 6。
取钱过程如下:
1.初始时,队列为 [1,2,3,4,5],第一个人需要 9 元钱,所以先取 6 元,然后去队尾继续排队。
2.此时,队列为 [2,3,4,5,1],第二个人需要 10 元钱,所以先取 6 元,然后去队尾继续排队。
3.此时,队列为 [3,4,5,1,2],第三个人需要 4 元钱,取完离队。
4.此时,队列为 [4,5,1,2],第四个人需要 7 元钱,所以先取 6 元,然后去队尾继续排队。
5.此时,队列为 [5,1,2,4],第五个人需要 2 元钱,取完离队。
6.此时,队列为 [1,2,4],这三个人还需取得的钱数均不超过 6 元,所以可以顺次取完离队。
离队顺序为 [3,5,1,2,4]。
题目分析:
我们最先想到的是模拟,但大数据是会超时的,所以要尽量避免模拟。
对于第i个人,他需要取a[i]/x(上取整)轮。第j轮的人一定比第j+1轮的人先走完。而在同一轮的人中,编号小的人会比编号大的人先离开。
所以我们可以先算出所有人的轮次号,再根据轮次号和编号进行排序,最后按顺序输出编号即可。
c++代码如下:
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=1e5+5;
struct p{
int rank,index;//rank是轮数,index是编号
}a[N];
bool cmp(p a,p b){
return a.rank<b.rank||(a.rank==b.rank&&a.index<b.index);
}
int t,n,x;
int main(){
scanf("%d",&t);
for(int i=1;i<=t;i++){
scanf("%d%d",&n,&x);
for(int j=1;j<=n;j++){
int m;
scanf("%d",&m);
a[j].rank=ceil((double)m/x);//ceil是上取整函数,例如ceil(3.4)=4
a[j].index=j;
}
sort(a+1,a+n+1,cmp);
printf("Case #%d: ",i);
for(int j=1;j<=n;j++) printf("%d ",a[j].index);
printf("\n");
}
return 0;
}