题目
题目描述: 小刚在玩JSOI提供的一个称之为“建筑抢修”的电脑游戏:经过了一场激烈的战斗,T部落消灭了所有z部落的入侵者。
但是T部落的基地里已经有N个建筑设施受到了严重的损伤,如果不尽快修复的话,这些建筑设施将会完全 毁坏。
现在的情况是:T部落基地里只有一个修理工人,虽然他能瞬间到达任何一个建筑,但是修复每个建筑都需要一定的时间。
同时,修理工人修理完一个建筑才能修理下一个建筑,不能同时修理多个建筑。
如果某个建筑在一段时间之内没有完全修理完毕,这个建筑就报废了。你的任务是帮小刚合理的制订一个修理顺序,以抢修尽可能多的建筑。
第一行是一个整数N接下来N行每行两个整数T1,T2描述一个建筑:修理这个建筑需要T1秒,如果在T2秒之内还没有修理完成,这个建筑就报废了。
输出一个整数S,表示最多可以抢修S个建筑.
N < 150,000; T1 < T2 < maxlongint
解析
这道题一看到题目,哇,老贪心了!我会做。然后理所当然的就错了。
贪心策略:
- 这道题的贪心策略已经都成套路了,一看就知道是按照建筑报废的顺序来排的(这一步确实没有问题,但是少了点东西)。
- 这时我们就会发现有些违和感,因为即使他更早报废了,但是这个建筑的修理时间浪费的太长了,对下面的准没有好处!
- 栗子:极端一点,来一个:100/100,50/101,51/102。一看就知道,用100那个绝对划不来啊。
- 但是我们的计算机也不可能直接给你判断,所以看了邓老师的题解,知道了这就是贪心的一种新的套路模式:给计算机留一步退路。
- 留退路是啥子意思呢?就是说我们的计算机如果感觉这玩意儿不好,就可以后悔,我就不要这个了。
- 还是上面的栗子:我们一开始要了100/100的,然后发现50/101的要不了了呀,按照我们原来的思想就是直接扔掉。
但是现在我们知道,50比100划得来,我们就拿50吧100换掉吧!(100就不要了) - 所以我们总结一下就是:
可以拿他和已经选取的建筑最大的比较一下,看哪个划得来。
如果这个被扔掉的更划得来,扔掉岂不是可惜了,那么这个时候就把这个换给待修复中最大的。
(你问我为啥是最大的?因为最大的在待修复的里面是最亏的呀) - 咋选最大的?当然不是一遍一遍的去找啊,这不是有堆呢吗,建个大根堆就好。
操作方法:
- 首先我们要让他按照报废顺序来排顺序(这个时候修理顺序也要排顺序),我们用结构体重载运算符+sort就好了。
- 然后呢,根据我们上面的讲解,我们需要一个大根堆,这里用一个优先队列就好了。
- 然后,如果碰到我们要的呢,我们就是给扔进堆里面。不要的呢,就和堆顶(最大的)进行一个比较,看留谁就好。
- 到了最后,留在堆里面的数据数量,自然就是答案了啦。
打代码咯:
- 首先输入数据,存在结构体里面。
- 然后按照上面讲的给结构体排个序。
- 接下来就对每个建筑进行判断,这里加一个time变量计算已经花费的时间就好了。
- 最后输出堆的大小就完事儿了。
- 看我代码咯~
AC代码
#include <iostream> #include <algorithm> #include <utility> #include <queue> using namespace std; #define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); //代码预处理区 const int MAX = 1e5 + 5e4 + 7; struct Node { int repair, time; bool operator < (const Node& v) const { if (time == v.time) return repair < v.repair; return time < v.time; } } info[MAX]; priority_queue<int, vector<int>, less<int> > pq; //全局变量区 int main() { IOS; int n; cin >> n; for (int i = 1; i <= n; i++) cin >> info[i].repair >> info[i].time; sort(info + 1, info + 1 + n); int time = 0; for (int i = 1; i <= n; i++) { if (time > info[i].time) continue;//已经报废了 if (time + info[i].repair <= info[i].time) { time += info[i].repair; pq.push(info[i].repair); } //如果可以修就直接修 else { if (pq.top() > info[i].repair) { time -= pq.top() - info[i].repair; pq.pop(); pq.push(info[i].repair); } } //修不了就判断他是不是比已经修了的中最划不来的划得来,划得来就换这个 } cout << pq.size() << endl; return 0; } //函数区