POJ-2184(变形的01背包解决2选择的最值问题)
题意:
每个奶牛有两个属性,分别是sn和fn,现在要求选择其中的一些奶牛,使的他们sn之和sgma(sn) 和fn之和sgma(fn) 的和最大,同时要求sgma(sn)和sgma(fn)都非负。
思路:
可以直接考虑0,1背包。把其中一个值当成限制条件,另外一个值当作dp值(这一点较为简单)
因为存在负数,所以数组需要便宜。全部值和为-100000~100000之间,所以偏移100000即可。(这一点也是常规操作)
接下来开始dp,状态转移较为简单,直接套用0,1背包,于是,我写完之后一直过不了样例,调试也不方便,于是我就百度了。。。(常规操作......
因为我们一位数组的0,1背包都是容量都是从大到小枚举(完全背包就是从小到大枚举)
为什么是这样?因为0,1背包只能选择一次,都是用比较大的容量的dp值来更新当前的dp,如果从小到大枚举,则存在多次更新。(可能没说清楚,可以画一下dp转移过程)
于是对于sn[i]的正负,状态转移方向不同,因为需要保证上面的顺序。
#include <iostream> #include<stdio.h> using namespace std; const int N=105; const int V=1000; int dp[2*N*V]; int sn[N]; int fn[N]; const int INF=0x3f3f3f3f; int main() { int n; scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d%d",&sn[i],&fn[i]); } for(int i=0;i<2*N*V;i++)dp[i]=-INF; dp[N*V]=0;//偏移N*V for(int i=0;i<n;i++){ if(sn[i]<=0&&fn[i]<=0)continue;//表示第i个一定没有贡献 if(sn[i]>0){//常规的01背包,逆向枚举 for(int j=2*N*V-1;j>=sn[i];j--){//j>=sn[i]防止越界 if(dp[j-sn[i]]==-INF)continue;//不需要更新 dp[j]=max(dp[j],dp[j-sn[i]]+fn[i]); } } else{ //正向枚举,保证不能用一个dp不能多次更新(01背包只能选一次) for(int j=0;j<=2*N*V-1+sn[i];j++){ if(dp[j-sn[i]]==-INF)continue; dp[j]=max(dp[j],dp[j-sn[i]]+fn[i]); } } } int ans=-INF; //保证i大于N*V,即这一维>=0 for(int i=N*V;i<N*V*2;i++){ if(dp[i]<0)continue;//保证非负 ans=max(ans,i+dp[i]-N*V); } printf("%d\n",ans); return 0; }
不疯魔不成活