终于等到你

众里寻他千百度,蓦然回首,那人却在灯火阑珊处。

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

 

posted @ 2019-09-19 20:00  gzr2018  阅读(177)  评论(0编辑  收藏  举报