POJ - 2184 Cow Exhibition (01揹包 中 負數的處理)
阿新 • • 發佈:2018-11-15
"Fat and docile, big and dumb, they look so stupid, they aren't much fun..." - Cows with Guns by Dana Lyons The cows want to prove to the public that they are both smart and fun. In order to do this, Bessie has organized an exhibition that will be put on by the cows. She has given each of the N (1 <= N <= 100) cows a thorough interview and determined two values for each cow: the smartness Si (-1000 <= Si <= 1000) of the cow and the funness Fi (-1000 <= Fi <= 1000) of the cow. Bessie must choose which cows she wants to bring to her exhibition. She believes that the total smartness TS of the group is the sum of the Si's and, likewise, the total funness TF of the group is the sum of the Fi's. Bessie wants to maximize the sum of TS and TF, but she also wants both of these values to be non-negative (since she must also show that the cows are well-rounded; a negative TS or TF would ruin this). Help Bessie maximize the sum of TS and TF without letting either of these values become negative.
Input
* Line 1: A single integer N, the number of cows * Lines 2..N+1: Two space-separated integers Si and Fi, respectively the smartness and funness for each cow.
Output
* Line 1: One integer: the optimal sum of TS and TF such that both TS and TF are non-negative. If no subset of the cows has non-negative TS and non- negative TF, print 0.
Sample Input
5
-5 7
8 -6
6 -3
2 1
-8 -5
Sample Output
8Hint OUTPUT DETAILS:
Bessie chooses cows 1, 3, and 4, giving values of TS = -5+6+2 = 3 and TF
= 7-3+1 = 5, so 3+5 = 8. Note that adding cow 2 would improve the value
of TS+TF to 10, but the new value of TF would be negative, so it is not
allowed.
struct Cow { int sm, fu;//牛的smart和funny }cow[maxn]; for (int i = 1; i <= n; i++) scanf("%d %d", &cow[i].sm, &cow[i].fu); for (int i = 0; i <= maxnv * 2; i++) dp[i] = -9999999; dp[maxnv] = 0;
2.關鍵的地方就在這裡了:還是負數導致的。我們來看狀態轉移方程:dp[j] = max(dp[j], dp[j - cow[i].sm] + cow[i].fu);紅色部分中,如果sm為負的話,-號就成了+號,逆序跟新,就會造成從前往後,而後面的是我們已經跟新過的,造成重複和錯誤跟新。所以我們要將其分開來。
for (int i = 1; i <= n; i++) { if (cow[i].sm > 0)//為正逆序 { for (int j = maxnv * 2; j >= cow[i].sm; j--) dp[j] = max(dp[j], dp[j - cow[i].sm] + cow[i].fu); } else//為負正序 { for (int j = 0; j - cow[i].sm <= maxnv * 2; j++) dp[j] = max(dp[j], dp[j - cow[i].sm] + cow[i].fu); } }
3.最後一個點在於輸出,這個我困了很久,理解起來挺有難度的,不過想一下還是會明白的。之前我們以100000為中間點,並作為跟新的基礎點。那很顯然在這個點的右邊,都是smart相加後為整數的點,那為什麼呢?我們就拿-5 7 /8 -6這兩個點解釋一下,-5是正序遍歷,也就是在100000點的左側,在靠近這個點的999995這個位置上存上7.其它位置認為負無窮。接著是8,逆序遍歷,在100008位置不會是1,因為100000位置是0,-6+0還是-6.當到了100003時,存1的值。所以只有smart相加為正時,才會出現在右側。接下來我們只要在找在右側的正數即可。你有沒有在上面的資訊中發現一個很重要的點呢?那就是j-100000就是j這個點所有smart的和,是不是很神奇。自己可以好好想想明白(相當於存位元組長度)。
for (int j = maxnv; j <= maxnv*2; j++) if (dp[j] > 0) ans = max(ans, dp[j] + j - maxnv);
AC程式碼:
#include<bits/stdc++.h>
using namespace std;
const int mid = 100000;
const int inf = 99999999;
const int N = 200005;
int v[105],w[105];
int dp[N];
int main()
{
int n;
while(scanf("%d",&n)==1){
for(int i=1;i<=n;i++)
scanf("%d%d",&w[i],&v[i]);
for(int i=0;i<N;i++) //初始化 因為有負數 所以初始化一個非常小的負數
dp[i]=-inf;
dp[mid]=0; //最開始的點
for(int i=1;i<=n;i++){
if(w[i]>0)
for(int k=N-1;k>=w[i];k--) //正數時候的揹包
dp[k]=max(dp[k],dp[k-w[i]]+v[i]);
else
for(int k=0;k-w[i]<N;k++) //負數時候的揹包
dp[k]=max(dp[k],dp[k-w[i]]+v[i]);
}
int sum=0;
for(int i=mid;i<N;i++)
if(dp[i]>0)
sum=max(sum,dp[i]+i-mid); //dp陣列存的是TF,通過i-mid計算TS
printf("%d\n",sum);
}
}