1. 程式人生 > 實用技巧 >Cow Exhibition (01揹包的負數處理)

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