当前位置:   article > 正文

0-1背包问题全解析_01背包问题解空间树

01背包问题解空间树

0-1背包问题
给定n个重量为w1/w2/w3.../wn,价值为v1/v2/v3.../vn的物品,容量为C的背包,求这个背包可以装下的价值最高的子集,每个物品只能使用一次

w = {2,1,3,2}   //重量
v = {12,10,20,15}   // 价值
C = 5   // 容量
#最佳子集为   [2,1,2]  12+10+15 = 37
  • 1
  • 2
  • 3
  • 4

暴力递归

对每个物品,都有选择/不选 两个状态,这样解空间就可以描述成一个状态树,

在这里插入图片描述
可以很像求子集的问题,不同的地方就是,子集的重量和需要小于背包重量,否则需要剪枝,如2->1->3不满足报容量,则3不可成为2->1这个子路径的叶节点,然后统计每一个子集的价值总和,比较得出最大值即可;

void backtrack(vector<int> w,vector<int> v,int C,int index,int& res,int val){

    if(index == w.size() || C<=0){
        res = max(res,val);
        return ;
    }
    
    if(C >= w[index]){
        backtrack(w,v,C-w[index],index+1,res,val+v[index]);
    }
    backtrack(w,v,C,index+1,res,val);
}

int main() {
    vector<int> w = {2,1,3,2};
    vector<int> v = {12,10,20,15};
    int res = 0,C=5;
    backtrack(w,v,C,0,res,0);
    cout<<res<<endl;
    return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

带备忘录的递归

因为上述方法会存在很多重复子问题的计算,所以我们可以保存子问题计算的结果,从而达到剪枝的效果

int backtrack2(vector<int> w,vector<int> v,int C,int index,int val,vector<vector<int>>& memo){
    if(index == w.size() || C <= 0){
        return val;
    }
    if(memo[index][C]!=-1)
        return memo[index][C];
    
    int aa = 0;
    if(C >= w[index]){
        aa = backtrack2(w,v,C-w[index],index+1,val+v[index],memo);
    }
    aa = max(aa,backtrack2(w,v,C,index+1,val,memo));
    
    memo[index][C] = aa;
    return aa;
}

int main() {
    vector<int> w = {2,1,3,2};
    vector<int> v = {12,10,20,15};
    int res = 0;

    vector<vector<int>> memo(w.size(),vector<int>(6,-1));
	res = backtrack2(w,v,5,0,0,memo);
    cout<<res<<endl;
    
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

动态规划

我们可以使用一个二维数组dp[n][C+1],表示第几个物体 在C容量下的价值;
dp[i][j] = max(dp[i-1][j] , dp[i-1][j-w[i] ] + v[i])
表示w[i]i个物体不装入背包的话,则直接由上一层价值传递,如果w[i]装入背包的话,则由dp[i-1][j-w[i]] 上一层 减去物体重量时的价值 + 物体本身的价值,取两者大,表格示意图如下:

	    | 背包容量
----------
物体id	|
  • 1
  • 2
  • 3

在这里插入图片描述

int dfs_dp(vector<int> w ,vector<int> v,int C){
    vector<vector<int>> dp(w.size(),vector<int>(C+1,0));

    for (int i = 0; i <= C; i++) {
        dp[0][i] = w[0] <= i ? v[0] : 0;
    }

    for(int i=1;i<w.size();i++){
        for(int j=1;j <= C;j++){
            dp[i][j] = dp[i-1][j];
            if( j >= w[i] ){
                dp[i][j] = max(dp[i][j],dp[i-1][j-w[i]]+v[i]);
            }
        }
    }
    for(auto i:dp){
        for(auto j:i)
            cout<<j<<" ";
        cout<<endl;
    }
    return dp[w.size()-1][C];
}

int main() {
    vector<int> w = {2,1,3,2};
    vector<int> v = {12,10,20,15};
    
    int res = 0;
    res = dfs_dp(w,v,5);

    cout<<res<<endl;
    return 0;
}

/*
0 0 12 12 12 12 
0 10 12 22 22 22 
0 10 12 22 30 32 
0 10 15 25 30 37 
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

状态空间压缩

leetcode 416 Partition Equal Subset Sum

给定一个仅包含正整数的非空数组,确定该数组是否可以分成两部分,要求两部分的和相等

[1,2,3,6] --> [1,2,3],[6]
  • 1
int dfs_dp(vector<int> w ,int C){
    //  抽象为: 使用n个(重量w)的物体,能否正好装满C容量的背包;
    // dp[n][c] 表示 使用n个物体,是否可以装满 C容量的背包;
    int n = w.size();
    vector<vector<bool>> dp(n,vector<bool>(C+1,false));
    
    dp[0][w[0]] = 1; // 初始,使用w[0]物体,可以正好装满w[0]容量的背包
    // dp[i][j] = dp[i-1][j]; 当前物体不入袋,继承上一个物体的状态
    // dp[i][j] = dp[i][j] || dp[i-1][j-w[i]]; 当前物体入袋||不入袋,只要有一个状态可以装满 即为True

    for(int i=1;i<n;i++){
        for(int j=0;j<=C;j++){
            dp[i][j] = dp[i-1][j];
            if(j>=w[i])
                dp[i][j] = dp[i][j] || dp[i-1][j-w[i]];
        }
    }
    
    for(auto i:dp){
        for(auto j:i)
            cout<<j<<" ";
        cout<<endl;
    }

    return dp[n-1][C];
}


int main() {
    vector<int> v1 = {1,2,3,6};

    bool flag = dfs_dp(v1,6);
    cout<<flag<<endl;
    return 0;
}
/*
0 1 0 0 0 0 0 
0 1 0 1 0 0 0 
0 1 0 1 1 0 1 
0 1 0 1 1 0 1 
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/333081?site
推荐阅读
相关标签
  

闽ICP备14008679号