询系--家庭作业

【问题描述】

        小明一共有n项作业,第i项作业要在di时间之前完成,小明完成第i项作业需要bi的时间,对于小明来说,喝奶茶可以提高工作效率,但是奶茶对于不同的作业功效是不同的,你可以认为,小明在做第i项作业期间,每喝1ml的奶茶,他完成第i项作业所需要的时间就会减少ai,当然,奶茶还不至于神奇到有时间倒流的功能。

        现在,小明想知道,他要按时完成所有作业最少要喝奶茶的毫升数。

【输入格式】

        第一行一个正整数;

        接下来n行每行三个正整数ai,bi,di;

【输出格式】

        输出一个小数,表示小明要按时完成所有作业最少要喝奶茶的毫升数。(保留两位小数)

【输入样例】

        2 

        20 50 100

        10 100 50

【输出样例】

          5.00

【数据范围】

对于20%的数据,n<=10,对于所有的i满足di<=100,ai=1;

对于40%的数据,n<=3000;

对于100%的数据,1<=n<=2*10^5.ai,bi,di<=10^6

【算法分析】

        首先,有一个很显然的结论:如果做了若干项作业,它们的相对顺序对后面的作业是没有影响的,所以我们可以先把作业按截至时间排序从前往后做。

        依次枚举作业,如果当前不喝奶茶单纯完成时间来不及,就吵之前做过的ai最大且bi>0作业不断喝,直到时间充足。

由于前面算法的瓶颈在于找ai最大,我们直接用堆来维护一下最大值就可以通过本题了(当然你也可以使用任何支持查询最大值的数据结构,比如线段树)‘

时间复杂度(nlogn)

【核心代码1】

sort(p+1,p+n+1,cmp);
for(ont i=1;i<=n;++i)
{
	tot+=p[i].b;Q.Push(i);
	if(tot>p[i].d)
	{
		while(tot-p[i].d>=p[top=Q.g[1]b])
		{
			Ans+=p[top].b/(double)p[top].a;
			tot-=p[top].b;Q.Pop();
		}
		if(tot-p[i].d>0)
		{
			Ans+=(tot-p[i].d)/(double)p[top].a;
			p[top].b-=tot-p[i].d;tot=p[i].d;
		}
	}
}

’【核心代码2】

struct heap{
	int g[m],len;
	inline void push(const int &res){
		g[++len]=res;
		int now =len,nxt=len>>1;
		while (nxt){
			if(p[res].a>p[g[nxt]].a)
			g[now]=g[nxt],nxt=(now=nxt)>>1;
		}
		g[now]=res;
	}
}
inline void pop(){
	g[i]=g[len--];
	while(nxt<=len){
		if(nxt<len && p[g[nxt[1]]].a>p[g[nxt]].a) naxt|=1;
		if(p[g[nxt]].a>p[res].a)
		g[now]=g[nxt],nxt=(now=nxt)<<1;
	}
	g[now]=res;
}Q;

int main(){
	n=get();Q.len =0;
	for(int i=1;i<=n;++i){
		p[i].a=get();
		p[i].b=get();
		p[i].d=get();
	}
	sort(p+1,p+n+1,cmp);
	for(int i=1;i<=n;++i){
		tot+=p[i].b;Q.push(i);
		if(tot>p[i].d){
			while (tot-p[i].d>=p[top=Q.g[1]].b)
			{
				Ans +=p[top].b/(double)p[top].a;
				tot-=p[top].b;Q.pop();
			}
			if(tot-p[i].d)>0{
				Ans+=(tot-p[i].d)/(double)p[top].a;
				p[top].b-=tot-p[i].d;   tot=p[i].d;
			}		
			
		}
	}
	printf("%.2lf\n",Ans);
	return 0;
	
}

上一篇:「CF612D」题解


下一篇:P7469 [NOI Online 2021 提高组] 积木小赛 题解