题目

题目描述:
小刚在玩JSOI提供的一个称之为“建筑抢修”的电脑游戏:经过了一场激烈的战斗,T部落消灭了所有z部落的入侵者。
但是T部落的基地里已经有N个建筑设施受到了严重的损伤,如果不尽快修复的话,这些建筑设施将会完全 毁坏。
现在的情况是:T部落基地里只有一个修理工人,虽然他能瞬间到达任何一个建筑,但是修复每个建筑都需要一定的时间。
同时,修理工人修理完一个建筑才能修理下一个建筑,不能同时修理多个建筑。
如果某个建筑在一段时间之内没有完全修理完毕,这个建筑就报废了。你的任务是帮小刚合理的制订一个修理顺序,以抢修尽可能多的建筑。

输入描述:
第一行是一个整数N接下来N行每行两个整数T1,T2描述一个建筑:修理这个建筑需要T1秒,如果在T2秒之内还没有修理完成,这个建筑就报废了。

输出描述:
输出一个整数S,表示最多可以抢修S个建筑.
N < 150,000;  T1 < T2 < maxlongint


解析

这道题一看到题目,哇,老贪心了!我会做。然后理所当然的就错了。

贪心策略
  1. 这道题的贪心策略已经都成套路了,一看就知道是按照建筑报废的顺序来排的(这一步确实没有问题,但是少了点东西)。
  2. 这时我们就会发现有些违和感,因为即使他更早报废了,但是这个建筑的修理时间浪费的太长了,对下面的准没有好处!
  3. 栗子:极端一点,来一个:100/100,50/101,51/102。一看就知道,用100那个绝对划不来啊。
  4. 但是我们的计算机也不可能直接给你判断,所以看了邓老师的题解,知道了这就是贪心的一种新的套路模式:给计算机留一步退路
  5. 留退路是啥子意思呢?就是说我们的计算机如果感觉这玩意儿不好,就可以后悔,我就不要这个了
  6. 还是上面的栗子:我们一开始要了100/100的,然后发现50/101的要不了了呀,按照我们原来的思想就是直接扔掉。
    但是现在我们知道,50比100划得来,我们就拿50吧100换掉吧!(100就不要了)
  7. 所以我们总结一下就是
    可以拿他和已经选取的建筑最大的比较一下,看哪个划得来
    如果这个被扔掉的更划得来,扔掉岂不是可惜了,那么这个时候就把这个换给待修复中最大的。
    (你问我为啥是最大的?因为最大的在待修复的里面是最亏的呀)
  8. 咋选最大的?当然不是一遍一遍的去找啊,这不是有堆呢吗,建个大根堆就好。

操作方法
  1. 首先我们要让他按照报废顺序来排顺序(这个时候修理顺序也要排顺序),我们用结构体重载运算符+sort就好了。
  2. 然后呢,根据我们上面的讲解,我们需要一个大根堆,这里用一个优先队列就好了。
  3. 然后,如果碰到我们要的呢,我们就是给扔进堆里面。不要的呢,就和堆顶(最大的)进行一个比较,看留就好。
  4. 到了最后,留在堆里面的数据数量,自然就是答案了啦。

打代码咯:
  1. 首先输入数据,存在结构体里面。
  2. 然后按照上面讲的给结构体排个序。
  3. 接下来就对每个建筑进行判断,这里加一个time变量计算已经花费的时间就好了。
  4. 最后输出堆的大小就完事儿了。
  5. 看我代码咯~


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;
}
//函数区