背包练习-混合背包 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
#include
#include
#include
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 > 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 << " "<<":"<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 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;
}

你可能感兴趣的:(acm水题)