原题链接:饿饿 饭饭 - 题目 - Daimayuan Online Judge
解题思路:首先,如果打饭数k与总饥饿一致,则直接结束,因为没有人饿着,总饥饿也没有小于打饭数。而如果总饥饿小于打饭数,直接输出-1。这是两个特判,可以节省时间。
如果不属于上方的特判,是正常情况,可以按照以下步骤模拟:
第一步,选取最小的饭量,将其乘以总人数,如果小于打饭量k,则k减去该值,然后总人数减去饭量最小的人数得到剩余人数。然后剩余人数乘以第二小的饭量。如此循环,直到剩余人数乘以当时的饭量大于剩余k时,记录当前饭量为maxx,进入下一步。模拟的是所有人进行了喂饭,吃饱的离开。
第二步,如果剩余k除以剩余人数大于1,则所有剩余人员的饭量减去该商。模拟的是所有人又进行了完整的喂饭且无人吃饱。
第三步,给剩余的每个人喂一次,看到哪个人为止,喂到饭的饭量-1。模拟的是不完整的喂饭,即最后一轮。
第四步,从上一步最后一个被喂的后一个人开始查询,如果剩余饭量大于maxx(因为maxx记录了第一步中剩下每个人实际吃了多少,大于maxx代表实际没吃饱),则输出序号,直到循环到上一步最后一个被喂的人为止。
AC代码:
#include<bits/stdc++.h>
using namespace std;
long long n,k,a[100010],maxx=0,zrs,bk,bs;
//n,k,a为题目本意,maxx为第一部中吃的饭,zrs储存剩余人数,bk储存减去总饭量的k,用于特判,bs记录从谁结束最后一口饭
set<long long> s;//记录有哪些饭量
unordered_map<long long,int> tong;//记录每个饭量的人数
int main(){
cin>>n>>k;//输入
zrs=n,bk=k;
for(int i=0;i<n;i++){
scanf("%lld",&a[i]);
s.insert(a[i]);
tong[a[i]]++;
bk-=a[i];
}
if(bk>0){//特判
cout<<-1;
return 0;
}
if(bk==0) return 0; //特判
for(auto i:s){//第一步
if((i-maxx)*zrs<=k){
k-=(i-maxx)*zrs;
zrs-=tong[i];
maxx=i;
}
else break;
}
if(k>=zrs){//第二步
long long buffer;
buffer=k/zrs;
for(int i=0;i<n;i++){
if(a[i]>maxx) a[i]-=buffer;
}
k-=buffer*zrs;
}
for(int i=0;k;i++,i%=n){//第三步
if(a[i]>maxx) a[i]--,k--;
if(a[i]>maxx) bs=i;
}
for(int i=bs+1;i<n;i++){//第四步
if(a[i]>maxx) cout<<i+1<<' ';
}
for(int i=0;i<=bs;i++){
if(a[i]>maxx) cout<<i+1<<' ';
}
return 0;
}