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; }