poj-2184 Cow Exhibition 01揹包變形-負數的處理(可擴展到dp處理負數問題?)

題目鏈接

poj-2184 Cow Exhibition

題目

Cow Exhibition
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 15086 Accepted: 6127

Description

"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

8

Hint

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. 

Source

題意

牛分爲2個屬性,智力的搞笑力,值的範圍都是[-1000,1000],牛的魅力值是該牛的智力和搞笑力的和,現在需要選N頭牛參加比賽,要求分別求出智力和搞笑力的總值,以及魅力值,答案要求得到魅力值最大的情況值是多少。同時,在得到最大魅力值的情況下,智力總和和搞笑力總和都不能爲負數。

題解

這個題其實第一眼看下去是能轉化成01揹包的模型的,主要的難點在負數的處理。關於01揹包其實很好解決,2個值,把智力當做“揹包空間”,搞笑力當做“價值”即可解決問題。如果不存在負數的情況下能夠直接A題。

可是現在有負數呀,所以這個題博主也是墨跡了很長時間纔看懂別人的博客(very vegetable),這裏勉強分析一波:

首先考慮範圍和負數的處理。如果我們想要將負數當做下標,最簡單的方法就是擴大值的範圍。例如:[-1000,1000],我們在儲存的時候可以改成[0,2000],這樣,[0,999]就是實際上的負數範圍,1000就是0,[1001,2000]就是正數範圍。所以,我們也可以分析出值的上限:100*1000*2,牛最多100頭,每頭牛最大值1000,負數部分轉化。

然後數組就可以寫出來了。現在考慮一下轉化爲01揹包的模型。將智力ts[i](第i頭牛的智力)當做“揹包空間”,將搞笑力tf[i](第i頭牛的搞笑力)當做“價值”。根據我們常用的01揹包的狀態轉移方程,可以得到:

for(int j = M-1;j >= ts[i];j--){
     dp[j] = Max(dp[j],dp[j - ts[i]] + tf[i]);
}

M就是2*100*1000,。其實上面的方程就是我們不考慮正數的時候的狀態轉移方程。當然這個是滾動數組的優化版,如果看不懂就先看看簡單的01揹包是如何將二維數組優化成一維數組吧,安利一下“揹包九講”。

上面的轉移方程可以看出,所謂的“價值”單單隻考慮了搞笑力,但是題目要求魅力值是搞笑力和智力的總和。這裏不用擔心,最後遍歷dp數組,將下標(智力)和值(搞笑力)相加就行了,判斷下最大值而已。

上面分析的是正數,現在考慮一下負數。首先上代碼:

for(int j = 0;j < M+ts[i];j++){
    dp[j] = Max(dp[j],dp[j - ts[i]] + tf[i]);
}

可以看出循環是正向的,而不是負向!這裏博主理解了好長時間啊!不過搞懂以後對01揹包二維降一維的理解也更深刻了點。我們降到一維,最主要的原因就是dp[j]實際上是上一個狀態的dp[j],我們狀態轉移方程就是將上一次狀態轉成當前狀態。所以正數的時候我們要逆向,因爲這樣能確保遍歷到dp[j]的時候我們獲取的是上一個狀態。負數亦然,j - ts[i],當ts[i]是正數,j - ts[i] < j,這樣,如果我們正向推,得到的是下面這個圖:


可以看出,a、b就是Max函數裏面的兩個變量,正向推的時候,b已經被更新了,不是上一個值,反向畫逆序的圖如下:


完美解決問題對吧,從而我們也能夠理解負數的問題了。j - ts[i],由於ts[i]是負數,所以:j - ts[i] >j,所以狀態轉移時需要的2個值,不是一個上一個左,而是一個上一個右,所以我們只能這樣操作:


所以就是要正向遍歷了。

負數的問題解決後,整個dp數組都解決了。現在考慮的就是最大值問題。首先要求智力和搞笑力分別總和都不能是負數。也就是說,下標不爲負,值不爲負。所以智力的問題就很好解決了,下標 < 100*1000的全部不考慮,因爲全是負數。值直接加個if語句判斷一下嘍,dp[i] < 0的全部不考慮。那麼dp[i]的魅力值就是:dp[i] + i - 100*1000。注意,以爲爲了存負數,所以我們整體是加了100000的,實際上要減去這個值。遍歷dp數組求答案即可。

C++ 258ms AC

#include<iostream>
#include<vector>
#include<string>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
#include<utility>
#include<queue>
#include<sstream>
#include<iterator>
#include<math.h>
#include<malloc.h>
#include<string.h>
#define TIME std::ios::sync_with_stdio(false)
#define LL long long
#define MAX 233
#define INF 0x3f3f3f3f

using namespace std;

int n;
int zero = 100000;
int M = 200005;
int ts[110], tf[110];
int dp[200005];

int Max(int a, int b) {
    return a > b ? a : b;
}

int main() {
    TIME;
    while (cin >> n) {
        for (int i = 0; i < n; i++) {
            cin >> ts[i] >> tf[i];
        }
        memset(dp, -INF, sizeof(dp));
        dp[zero] = 0;
        for (int i = 0; i < n; i++) {
            if (ts[i] > 0) {
                for (int j = M - 1; j >= ts[i]; j--) {
                    if (dp[j - ts[i]] > -INF) {
                        dp[j] = Max(dp[j], dp[j - ts[i]] + tf[i]);
                    }
                }
            }else {
                for (int j = 0; j < M + ts[i]; j++) {
                    if (dp[j - ts[i]] > -INF) {
                        dp[j] = Max(dp[j], dp[j - ts[i]] + tf[i]);
                    }
                }
            }
        }
        int ans = 0;
        for (int i = zero; i < M; i++) {
            if (dp[i] > 0) {
                ans = Max(ans, dp[i] + i - zero);
            }
        }
        cout << ans << endl;
    }

    system("pause");
    return 0;
}

一個小優化

上面分析的時候,主要代碼都正確,但是在寫狀態轉移方程的時候加了一個小小的優化,當值是-INF的時候說明是不考慮這個數的,所以直接跳過。不過實測優化不了多長時間,290ms降到260ms。。。。大家忽略吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章