• 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;
    }
  • 相关阅读:
    开发入门
    Web开发的四个域
    JSP语法
    JSP入门
    变量的作用范围
    面向对象
    C#编译执行过程
    css3的渐变、背景、过渡、分页
    css3选择器总结
    css3基础选择器
  • 原文地址:https://www.cnblogs.com/lipu123/p/14015195.html
Copyright © 2020-2023  润新知