建筑抢修【题解】

题意描述

小刚在玩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

题解

通过数据范围可以发现dp无法求解。只能考虑贪心。这是一道非常经典的贪心(后悔贪心),所以有必要详细探究。

首先是为什么排序,按什么排序的问题。

我们发现在贪心中如果这一位现在无法选,将来也就选不到了,这就说明需要满足局部最优解。当时随意顺序下,我们发现这一位尽管不可能替代之前的某一次操作,但是可以替代后来的一次操作的。这就是为什么不排序不行。(注:w为最大等待时间,time为修理时间或当前时间)
在考虑排序时,我们发现可以后悔需要满足两个条件
1. t i m e n o w + t i m e i t i m e t o p < t i m e n o w 等价于 t i m e i < t i m e t o p
2. 在条件一的满足下,若满足 w i > w t o p 那么一定是可以取代的
可以得出再按w从大到小的排序下
如果连堆顶都替代不了那么其它的已经被修好的也就无法替代了
可以替代后面的当且仅当
1. 后面的值得代替也就是说后面的某一个可以代替堆顶或直接加入堆。
2. 如果是因为后面的代替了堆顶而去代替,那么自己也可以代替它代替的堆顶。显然自己不行
3. 如果是因为后面的可以直接加入堆是因为当前时间未到其报废时间。为了能在那时代替,那么需要自己的时间比后面可以直接加入的那个更晚。如果这样,不如自己早就加入了

这是就说明了没有后效性。此时要做的就只有按w排序,建以time为关键字的大根堆

代码

#include<bits/stdc++.h>
using namespace std;
template <class T>
inline void read(T&data){
    data=0;
    register char ch=0;
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch<='9'&&ch>='0'){
        data=(data<<3)+(data<<1)+(ch^48);
        ch=getchar();
    }
    return;
}
const int _ = 150010;
int n;
typedef long long LL;
struct tx{
    LL t,w;
}a[_];
struct node{
    LL u,v;
    bool operator < (const node& gh)const {
        return u<gh.u;
    }
};
priority_queue<node>Q;

bool cmp(tx x,tx y){
    return x.w<y.w;
}
int main(){ 
    read(n);
    for(register int i=1;i<=n;++i)read(a[i].t),read(a[i].w);
    sort(a+1,a+n+1,cmp);
    LL cnt=0;int ans=0; 
    for(register int i=1;i<=n;++i){

        if(cnt+a[i].t>a[i].w){
            if(Q.empty())continue ;
            register node f=Q.top();
            if(a[i].t<f.u){
                Q.pop();
                Q.push((node){a[i].t,a[i].w});
                cnt=cnt-f.u+a[i].t;     
            }
        }
        else {
            Q.push((node){a[i].t,a[i].w});
            cnt+=a[i].t;++ans;
        }
    }
    cout<<ans<<endl;
}

总结

这道题难不在已知结果求证明,而在不知结果时的结论。就算看出了是贪心,仍有许多排序和建堆的方法需要摸索。

猜你喜欢

转载自blog.csdn.net/jh_2002/article/details/81138576