揹包練習-混合揹包 AreYouBusy HDU - 3535

這是我wa了一道很久的揹包題目.
關鍵點在於我錯在了理解分組揹包.

本題有三種類型的集合.第一種是至少選擇其中一個.關於至少選擇一個的揹包可以參考D - I love sneakers! HDU - 3033這道題目.
之多選擇一個的揹包.還有01揹包.

在處理這三種集合的時候我選擇了分類,先處理了至少選擇一個揹包,他可以先預判斷哪些是不可能的.
狀態轉移方程:
dp[i][k]=max{ dp[i][k],dp[i-1][k-cost[j]]+val[k],dp[i][k-cost[j]]+val[j] }
這樣可以預判斷出哪些是不可能的並直接返回-1.

下面就是我wa了好久的地方了.揹包九講中闡述到分組揹包的一般寫法:

for 所有的組k
    for v=V..0
        for 所有的i屬於組k
            f[v]=max{f[v],f[v-c[i]]+w[i]}

我就按此寫了一發,隨後wa了近30發.
我對於分組揹包瞭解的甚少.總以爲他是一維就可以解決的.
我們對原狀態的更新的狀態方程(錯誤的):
dp[i][k]=max{ dp[i][k],dp[i][k-cost[j]]+val[k]}
我們對原狀態進行了更新後就無法再次保證選取物品時還是否是第一次.我們已經做出了不可逆的修改.
將狀態方程調製如下,保證了原狀態.
dp[i][k]=max{ dp[i][k],dp[i-1][k-cost[j]]+val[k]}

這個狀態方程是容易理解的.
對於一般的分組揹包,我們採用下面的狀態轉移方程:
f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i屬於組k}

但是對於本題,我們選取物品時是先要進行判斷是否可以選取,再做max修正的.
可以考慮先複製一遍就變成了這個方程.
dp[i][k]=max{ dp[i][k],dp[i-1][k-cost[j]]+val[k]}

而且幸運的是,複製後的方程已經k,j沒有關係了,他一直再做一個鬆弛的工作,調整for語句順序又可以少些一些判斷.

我初始化爲-inf,而不是-1的原因就是爲了排除 至少取一種的時候g==0,這樣dp==-1,合法情況被忽略了.

這裏對於 c==0時的判斷的順序也是重要的,否則會自身加兩次.

最後的代碼是:


#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
int k,s,g,c;
int n,T;
struct thing{
    int v,w;
    thing(){}
    thing(int v, int w):v(v),w(w){}
};
vector<vector<thing> > things[3];

int dp[200][200];
int cont;
void init(){
    for(int i = 0; i < 3; i++){
        things[i].clear();
    }
}

void test(){
    for(int i = 0; i < 3; i++){
        for(int j = 0; j < things[i].size(); j++){
            for(int k = 0; k < things[i][j].size() ; k++){
                cout << things[i][j][k].v << " "<<":"<<things[i][j][k].w<< endl;
            }
        }
    }
}
void at_least(){
    for(int i = 1; i <= things[0].size(); i++){
        for(int j = 0; j < things[0][i-1].size(); j++){
            int price = things[0][i-1][j].v;
            int value = things[0][i-1][j].w;
            for(int k = T; k >= price; k--){
                if(dp[i][k-price]>=0){
                    dp[i][k] = max(dp[i][k], dp[i][k-price]+value);
                }
                if(dp[i-1][k-price]>=0){
                    dp[i][k] = max(dp[i][k], dp[i-1][k-price]+value);
                }
            }
        }
    }
}

void at_most(){
    cont++;
    for(int i = 0; i < things[1].size(); i++){
        for(int k = T; k >= 0; k--){
            dp[cont+i][k] = dp[cont+i-1][k];
        }
        for(int j = 0; j < things[1][i].size(); j++){
            for(int k = T; k >= things[1][i][j].v; k--){
                if(dp[cont+i-1][k-things[1][i][j].v]>=0){
                    dp[cont+i][k] = max(dp[cont+i][k], dp[cont+i-1][k-things[1][i][j].v]+things[1][i][j].w);
                }
            }
        }
    }
    cont += things[1].size();
    cont--;
}

void ZeroOnePack(){
    for(int i = 0; i < things[2].size(); i++){
        for(int j = 0; j < things[2][i].size(); j++){
            for(int k = T; k >= things[2][i][j].v; k--){
                if(dp[cont][k-things[2][i][j].v]>=0){
                    dp[cont][k] = max(dp[cont][k], dp[cont][k-things[2][i][j].v]+things[2][i][j].w);
                }
            }
        }
    }
}

void Input(){
    for(int i = 0; i < n; i++){
        scanf("%d%d",&k,&s);
        vector<thing> temp;
        for(int j = 0; j < k; j++){
            scanf("%d%d",&c,&g);
            temp.push_back(thing(c,g));
        }
        things[s].push_back(temp);
    }
}

int main(){
    while(~scanf("%d%d",&n,&T)){
        init();
        Input();
        cont = things[0].size();
        memset(dp, -0x3f3f3f3f,sizeof dp);
        memset(dp[0], 0, sizeof dp[0]);
        int ans;
        at_least();
        if(dp[cont][T]<0){
            puts("-1");
            continue;
        }
        at_most();
        ZeroOnePack();
        if(dp[cont][T]<0)ans = -1;
        else ans = dp[cont][T];
        cout << ans << endl;
    }
    return 0;
}

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