YBTOJ 家庭作业(堆的应用)
一个很水的大根堆+贪心。
考虑将所有作业先按照截至日期升序排序,依次加入tot中,同时插入堆中,堆是在a意义下的大根堆。
若tot大于当前作业的截止日期,则取出之前以加入的作业中a最大的作业,进行加速,直到tot=当前作业的截止日期。
正确性很显然:如果不让截止日期小的作业尽量早地完成,则消耗很可能更大。
Code
tips1:我用的是手写堆,因为不太喜欢用重载运算符。
有人喜欢阴间的pair嵌套,但我不说是谁
tips2:一个有意思的bug:
当你想输出一个数据,结果输出的结果是"nan"时,很可能是因为在某一步一个数0除。
"nan"的含义是“not a number”,并不是出数据的人提醒你这道题很难。
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int n,len;
double tot=0,sum=0;
struct mint
{
double a,d,b;
}sav[maxn],heap[maxn];
bool cmp(mint x,mint y)
{
return x.d < y.d;
}
void insert(mint k)
{
heap[++len]=k;
// printf("%lf %lf %lf\n",k.a,k.b,k.d);
int pla=len;
while(pla>1)
{
int fa=(pla>>1);
if(heap[fa].a>heap[pla].a) return;
swap(heap[fa],heap[pla]);
pla=fa;
}
}//a是价值,b是所需时间,d是截止日期
mint get()
{
mint ans=heap[1];
heap[1]=heap[len--];
int pla=1;
while(pla*2<=len)
{
int son=pla<<1;
if(son+1<=len&&heap[son+1].a>heap[son].a) son++;
if(heap[son].a<heap[pla].a) break;
swap(heap[son],heap[pla]);
pla=son;
}
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%lf%lf%lf",&sav[i].a,&sav[i].b,&sav[i].d);
sort(sav+1,sav+n+1,cmp);
for(int i=1;i<=n;++i)
{
mint k=sav[i];
tot+=k.b;
insert(k);
if(tot<k.d) continue;
double deta=tot-k.d;
while(deta>0)
{
mint g=get();
if(g.b>=deta)
{
// printf("g.a=%lf",g.a);
sum+=deta/g.a;
g.b-=deta;
if(g.b!=0) insert(g);
deta=0;
tot=k.d;
}
else
{
// printf("g.a=%lf",g.a);
sum+=g.b/g.a;
tot-=g.b;
deta-=g.b;
}
}
}
printf("%.2lf",sum);
return 0;
}