【[JSOI2007]建筑抢修】

各种瞎写

之后也不知道为什么就过了

刚看到这道题感觉确实是不会的,因为我贪心太差了(QAQ)

之后就随便(yy)

发现首先我们得排一下序,以(t2)也就是建筑的损坏时间为第一关键字从小到大排序

这个还是比较好考虑的,我们得优先照顾一下那些在最开始就会损坏的建筑

那之后呢,好像我们现在可以做一个(dp)

我们设(dp[i][j])表示在前(i)个里选择(j)个的最小时间是多少

这个(dp)的状态数就是(n^2)的,尽管我们可以(O(1))转移,但是还是炸了

我们考虑一下贪心

我们还是按照刚才的排序来考虑

显然这样直接按照排出来的顺序扫一遍是不行的,我们得给前面的选择一个反悔的机会

于是我们开一个大根堆,用来存储之前选择的那些建筑的修筑时间

我们一旦新插入一个建筑,如果这个建筑可以被修筑,也就是(t1_i+tot<=t2_i)(tot)表示之前所选取的建筑的总时间和,那么我们就让这个建筑被修建

否则的话,我们把这个建筑和堆顶比较一下,看一看能否用这个元素取代堆顶,使得再删除堆顶的情况下这个建筑能否被选上,能的话就选上,否则就不选

至于这样为什么对呢,每一个建筑的价值都是(1),所以对于第二种情况来说它入堆的话就必须保证这个建筑能被选上且让这个建筑在被选上的情况下总时间最短,否则就还不如不选这个建筑

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define LL long long 
#define re register
#define maxn 150005
#define max(a,b) ((a)>(b)?(a):(b))
struct Node
{
	int t1,t2;
}a[maxn];
std::priority_queue<int> q;
inline int cmp(Node K,Node M)
{
	return K.t2<M.t2;
}
inline int read()
{
	char c=getchar();
	int x=0;
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9')
		x=(x<<3)+(x<<1)+c-48,c=getchar();
	return x;
}
int n;
int now=0,ans=0;
int main()
{
	n=read();
	for(re int i=1;i<=n;i++)
		a[i].t1=read(),a[i].t2=read();
	std::sort(a+1,a+n+1,cmp);
	LL tot=0;
	for(re int i=1;i<=n;i++)
	{
		if(tot+a[i].t1<=a[i].t2)
		{
			tot+=a[i].t1;
			now++;
			q.push(a[i].t1);
			ans=max(ans,now);
		}
		else
		{
			if(a[i].t1>q.top()) continue;
			if(a[i].t1+tot-q.top()<=a[i].t2)
			{
				tot-=q.top();
				q.pop();
				tot+=a[i].t1;
				q.push(a[i].t1);
			}
		}
	}
	std::cout<<ans;
	return 0;
}
原文地址:https://www.cnblogs.com/asuldb/p/10206166.html