Cow Exhibition (01背包的负数处理)

传送门

我的传送门

奶牛想证明他们是聪明而风趣的。为此,贝西筹备了一个奶牛博览会,她已经对N头奶 牛进行了面试,确定了每头奶牛的智商和情商。 贝西有权选择让哪些奶牛参加展览。由于负的智商或情商会造成负面效果,所以贝西不 希望出展奶牛的智商之和小于零,或情商之和小于零。满足这两个条件下,她希望出展奶牛 的智商与情商之和越大越好,请帮助贝西求出这个最大值Input第一行:一个整数N,表示奶牛的数量, 1 ≤ N ≤ 100 第二行到第N + 1行:第i + 1行有两个用空格分开的整数: Si和Fi,分别表示第i头奶牛的智商和情商, -1000 ≤ Si ≤ 1000, -1000 ≤ Fi ≤ 1000Output第一行:单个整数,表示情商与智商和的最大值。贝西可以不让任何奶牛参加展览,如 果这样应该输出0Sample Input

5
-5 7
8 -6
6 -3
2 1
-8 -5

Sample Output

8

Hint选择 1, 3, 4 号奶牛,此时智商和为-5 + 6 + 2 = 3,情商和为7 - 3 + 1 = 5。加 入 2 号奶牛可使总和提升到10,不过由于情商和变成负的了,所以是不允许的

 

我们可以写出dp[2]=3; dp[8]=4;就是这个道理,最后再把8+4=12就行了。
我们令dp[100000]=0,因为其他的值都有可能是负数,所以我们把其他的都赋值为负无穷。
以100000作为原点,这样dp括号里的容量就不会为负数了

如同01背包一样,如果TS[i]是正数,则有如下循环:
for(i=0;i<n;i++)
   {
       if(TS[i]>0)
           for(j=200000;j>=TS[i];j--)
           dp[j]=max(dp[j],dp[j-TS[i]]+TF[i]);
}
如果是负数,则是:
for(i=0;i<n;i++)
   {
       if(TS[i]<=0)
   for(j=0;j-TS[i]<=200000;j++)
           dp[j]=max(dp[j],dp[j-TS[i]]+TF[i]);
   }

为什么TS[i]为负数是要用正序循环
在标准的01背包里,我们看见j>j-TS[i],也就是说我们要想求dp[j],我们必须用到上一层的dp[j]和dp[j-TS[i]],也就是要用到上一层的他自己dp[j]与自变量j-TS[i]小于自己的[j]的dp[j-TS[i]],如果正序的话,dp[j-TS[i]]就会被破坏掉。
同样的,如果TS[i]是负数,j-TS[i]>j,如果我们要求dp[j],我们要用到上一层的dp[j]与上一层的自变量j-TS[i]大于j的dp[j-TS[i]],如果按照逆序循环,dp[j-TS[i]]会被破坏掉。

 for(j-TS[i]=200000;j>=0;j--)
           dp[j]=max(dp[j],dp[j+TS[i]]+TF[i]);

 

这也是不可以的。
因为当TS[i]是负数的时候,dp[j]括号中的这个容量应该由大容量推来。比如说你要算dp[100003],那你就应该由dp[100004]等等推来。也就是说你要保证max中的后边的dp括号中的自变量要大于前边的dp。
下面给出文章开头例子的计算过程:

第一次,s[0]=-5<0;
可得
{
dp[99995]=7;
dp[100000]=0;
}
第二次,s[1]=8>0
{
dp[99995]=7;
dp[100000]=0;
dp[100003]=1
};
第三次,s[2]<0;
{
dp[99993]=10;
dp[99995]=7;
dp[100003]=1;
dp[100001]=4;
}

最后说明求总和的最大值:

因为我们要保证TS[i]和TF[i]的和分别都大于0,TS[i]的和用dp[]括号里的值是否大于100000来保证,只要大于100000,说明TS[i]的总和没有是负数。这个是显而易见的,比如说dp[99995]=7,代表在TS[i]的和是-5的情况下,所获的最大价值是7.
再就是保证TF[i]的和大于0,这个通过dp[]的值来保证,值大于0说明TF[i]总和大于0.
所以我们利用如下循环,就可以求出总和的最大值:

 for(i=100000;i<=200000;i++)
    {
        if(dp[i]>=0)
            l=max(l,i+dp[i]-100000);
    }

转载自

下面是我自己的理解

就是说数组的下标不可负数,那就让它有一个偏移量(就是说再设置一个起点100*1000为0点)

正数的时候反着跟更新,负数的时候正着更新

 

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=5e5+100;
int a[maxn];
int b[maxn];
int dp[maxn];//dp[i][j]指的是 
int n;
int main(){
    cin>>n;
    memset(dp,-INF,sizeof(dp));
    for(int i=1;i<=n;i++){
        cin>>a[i]>>b[i];
    }
    dp[100000]=0;
    for(int i=1;i<=n;i++){
        if(a[i]>0){
            for(int j=200000;j>=a[i];j--){
                dp[j]=max(dp[j],dp[j-a[i]]+b[i]);
            }
        }
        else{
            for(int j=0;j<=200000+a[i];j++){
                dp[j]=max(dp[j],dp[j-a[i]]+b[i]);
            } 
        }
    }
    int ans=0;
    for(int i=100000; i<=200000;i++){
        if(dp[i]>0){
            ans=max(ans,dp[i]+i-100000);
        } 
    }
    cout<<ans<<endl;
    return 0;
}

 

posted @ 2020-11-21 12:08  哎呦哎(iui)  阅读(132)  评论(0编辑  收藏  举报