BZOJ3174 TJOI2013 拯救小矮人 贪心+一般DP

题意:有N个小矮人,他们从脚到肩膀的高度Ai,胳膊长度为Bi。陷阱深度为H。如果我们利用矮人1,矮人2,矮人3……矮人k搭一个梯子,满足A1+A2+A3+….+Ak+Bk>=H,那么矮人k就可以离开陷阱逃跑了,一旦一个矮人逃跑了,他就不能再搭人梯了,问最多可以使多少个小矮人逃跑。

题解:

由于不管怎么排,总的高度是不变的,因此关键在于如何找出出洞的次序。

考虑只有a和b两个小矮人,那么如果a先出那么剩余高度就是b.a+b.b,如果b先出那剩余高度就是a.a+a.b,因此关键在于b.a+b.b与a.a+a.b的比较。

当然如果a.a+a.b>b.a+b.b,那肯定让b先出。有没有特殊情况?有。

若a.a+a.b>b.a+b.b,但a.a+b.a+b.b<a.a+b.a+a.b,则假定a.a>a.b+b.b,则a.a+b.a+b.b<a.b+b.a<b.b+a.b+a.a。因此无论怎样,如果走掉a之后剩余的高度比b先走掉剩余的高度大,则一定让a先走掉。

因此将每个小矮人按照a+b排序之后按顺序DP即可。

定义f[i]为走掉i个小矮人之后剩余的最大高度,状态转移方程f[j+1]=max(f[j+1],f[j]-man[i].a),转移的条件是f[j]+man[i].b>=H(即这个小矮人能走掉)。

当f[ans+1]>=0时,ans++。

初始化f[1->n]=-1,f[0]=sum(man[i].a)。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN=2000+2;
struct MAN{
    int a,b;
}man[MAXN];
int H,N,f[MAXN],ans;

bool cmp(MAN x,MAN y){return x.a+x.b<y.a+y.b;}

int main(){
    memset(f,-1,sizeof(f));
    f[0]=0;

    cin >> N;
    for(int i=1;i<=N;i++){
        cin >> man[i].a >> man[i].b;
        f[0]+=man[i].a;
    }
    cin >> H;

    sort(man+1,man+N+1,cmp);

    for(int i=1;i<=N;i++)
        for(int j=ans;j>=0;j--){
            if(f[j]+man[i].b>=H) f[j+1]=max(f[j+1],f[j]-man[i].a);
            if(f[ans+1]>=0) ans++;
        }

    cout << ans << endl;

    return 0;
}
View Code

 

posted @ 2017-02-28 01:52  WDZRMPCBIT  阅读(192)  评论(0编辑  收藏  举报