[JSOI2007]建筑抢修
题目描述
小刚在玩JSOI提供的一个称之为“建筑抢修”的电脑游戏:经过了一场激烈的战斗,T部落消灭了所有z部落的入侵者。但是T部落的基地里已经有N个建筑设施受到了严重的损伤,如果不尽快修复的话,这些建筑设施将会完全毁坏。现在的情况是:T部落基地里只有一个修理工人,虽然他能瞬间到达任何一个建筑,但是修复每个建筑都需要一定的时间。同时,修理工人修理完一个建筑才能修理下一个建筑,不能同时修理多个建筑。如果某个建筑在一段时间之内没有完全修理完毕,这个建筑就报废了。你的任务是帮小刚合理的制订一个修理顺序,以抢修尽可能多的建筑。
输入输出格式
输入格式:
第一行是一个整数N,接下来N行每行两个整数T1,T2描述一个建筑:修理这个建筑需要T1秒,如果在T2秒之内还没有修理完成,这个建筑就报废了。
输出格式:
输出一个整数S,表示最多可以抢修S个建筑.
输入输出样例
输入样例#1
4
100 200
200 1300
1000 1250
2000 3200
输出样例#1
3
说明
N < 150,000; T1 < T2 < maxlongint
堆 + 贪心
题目给了两个限制条件
一个是修建时间
另一个是最晚修建期限
显然两个关键字不能贪心
所以我们想办法消除一个关键字
按照最晚修建期限从小到大排序!
这样就可以保证后面处理的建筑可以随意替代前面修建的任意一个建筑
我们就可以开个堆贪心了
按照时间花费维护一个大根堆
再维护一个endtime表示修建完前面的建筑后的时间
然后我们就对于一个建筑
尽量早修理
如果\(End[i] - Cost[i] < endtime\) 说明目前这种方案是不能修理这个建筑的
这时我们就要从大根堆中拿出Cost最大的元素x
如果 \(Cost[x] < Cost[i]\) 那就不修这个建筑了
否则就用这个建筑去代替x那个建筑
因为我们已经按照End排序了
所以之前的建筑的End一定小于当前建筑的End
直接替代并把endtime减去两者的修筑时间差\((Cost[x] - Cost[i])\)即可
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 150005 ;
using namespace std ;
inline int read() {
char c = getchar() ; int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
return x*w ;
}
int n , Ans , endtime ;
struct Building { int Cost , End ; } p[M] ;
struct Node { int id ; } ;
priority_queue < Node > q ;
inline bool operator < (Building a , Building b) { return a.End < b.End ; }
inline bool operator < (Node a , Node b) { return p[a.id].Cost < p[b.id].Cost ; }
int main() {
n = read() ; endtime = 0 ;
for(int i = 1 ; i <= n ; i ++)
p[i].Cost = read() , p[i].End = read() ;
sort(p + 1 , p + n + 1) ;
for(int i = 1 ; i <= n ; i ++) {
if(p[i].End - p[i].Cost >= endtime) {
endtime = endtime + p[i].Cost ;
q.push((Node) { i }) ;
++ Ans ;
}
else if(!q.empty()) {
int x = q.top().id ;
if(p[x].Cost > p[i].Cost) {
q.pop() ;
endtime = endtime - (p[x].Cost - p[i].Cost) ;
q.push((Node) { i }) ;
}
}
}
printf("%d\n",Ans) ;
return 0 ;
}