题意: 有n个喝咖啡的时间,每天只有m的时间工作,两次喝咖啡的时间间隔必须大于等于d分钟,要把所有n个咖啡时间都喝掉最少需要几天
题解: 首先大体思路是,将咖啡时间按从小到大排序,可以明确的是,因为要保证两两相邻的喝咖啡时间要遵守大于等于d的限制,因此必须合理安排使得任意两个间隔小于d的喝咖啡时间错开。想到这样的方法,取出一个最小时间,可以在第一天喝,取出一个次小时间,若与第一天中的最后一次喝咖啡时间间隔大于等于d,那么这次可以在第一天内喝掉,不用在下一天喝,否则只能错开在下一天喝。
因为要喝掉一次咖啡时间肯定选择已使用天数中截止时间最小的,这样才能保证最大化的利用每天的时间,所以优先队列维护top使其作为每次更新的那一天的进行时间。
如果连top的时间都无法容纳下当前遍历的咖啡时间,那么说明需要新开一天,并且,每次弹出超过一天工作时间的天数。
#include<bits/stdc++.h>
#define LL long long
#define M(a,b) memset(a,b,sizeof a)
#define pb(x) push_back(x)
using namespace std;
const int maxn=2e5+7;
struct node
{
int pos,ti;
bool operator <(const node &a)const
{
return ti<a.ti;
}
} a[maxn];
struct days
{
int day,tim;
days() {}
days(int a,int b)
{
tim=a,day=b;
}
bool operator <(const days &a)const
{
return tim>a.tim;
}
};
int n,m,d,ans[maxn];
priority_queue<days>q;
int main()
{
scanf("%d%d%d",&n,&m,&d);
for(int i=0; i<n; i++) scanf("%d",&a[i].ti),a[i].pos=i;
sort(a,a+n);
int cnt=0;
for(int i=0; i<n; i++)
{
while(!q.empty()&&q.top().tim>=m)q.pop();
if(i==0||q.top().tim>=a[i].ti)//新开一天
{
ans[a[i].pos]=++cnt;
q.push(days(a[i].ti+d,cnt));
}
else
{
int tmp=q.top().day;
q.pop();
ans[a[i].pos]=tmp;//第pos个咖啡时间在第top天喝掉
q.push(days(a[i].ti+d,tmp));//更新掉第top天的最新咖啡时间
}
}
printf("%d\n",cnt);
for(int i=0; i<n; i++)printf("%d%c",ans[i],i==n-1?'\n':' ');
}
/*
10 10 1
10 5 7 4 6 3 2 1 9 8
1 2 3 4 5 6 7 8 9 10
1 2 1 2 1 2 1 2 1 2
*/