常见的背包问题:01背包和完全背包

1. 01背包

有 n 件物品和容量为 m 的背包,给出 n 件物品的重量 w[i] 以及价值 c[i] ,求解让装入背包的物品重量不超过背包容量且价值最大,每个物品只能选择一次 。

暴力解法:暴力枚举每件物品要不要放入背包,时间复杂度O(2^n),显然是不能接受的,而使用 DP 可以将时间复杂度变成O(nm)

动态规划:用 dp[i][j] 表示,前 i 件物品放入容量为 j 的背包中所能获得的最大价值,很明显最终的 dp[n][m] 即为所求的结果,那么怎么求解 dp[i][j] 呢?

  1. 不放第 i 件物品,问题转换为前 i - 1 件物品放入容量为 j 的背包中所能获得的最大价值,即 dp[i][j] = dp[i - 1][j] 。
  2. 放第 i 件物品,问题转换为前 i - 1 件物品放入容量为 j - w[i] 的背包中所能获得的最大价值,即 dp[i][j] = dp[i - 1][j - w[i]] 。
  3. 要求最大价值,则 dp[i][j] = max(dp[i - 1][j],dp[i - 1][j - w[i]] + v[i])
  4. 求出背包中最终装的物品。
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;

const int MAX_N = 1e3;
const int MAX_M = 1e4;

int dp[MAX_N][MAX_M];  
int w[MAX_N];
int v[MAX_N];
vector<int> choice;
int n, m;

int main(){
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i){
        scanf("%d", &w[i]);
    }
    for(int i = 1; i <= n; ++i){
        scanf("%d", &v[i]);
    }

    //0-1背包问题,对于每一个物品
    //用dp数组中每一项dp【j】表示循环中的前i件物品,在容量为j的背包中的最大价值
    //用双层循环,构建一个前i件物品、容量0-m的表,最终表的右下角就是问题的答案
    //可以为了节省空间,省略了二维数组的行,因为每一次多一个物品之后,答案只会受前一行影响
    //先将边界,0件物品时候的价值设为0,因为要求所选择的物品,用二维数组
    for(int i = 0; i <= m; ++i){
        dp[0][i] = 0;
    }
    for(int i = 0; i <= n; ++i){
        dp[i][0] = 0;
    }

    //循环构建表
    //1.当前背包容量不够放该物品,不放
    //2.当前容量够放该物品,看放不放的价值大小
    //放的话,用背包容量减去该物品重量,回到前dp[i-1][j-w[i]] + v[i]
    //比较放与不放的价值大小,选择大的价值的更新dp[i][j]
    for(int i = 1; i <= n; ++i){
        for(int j = m; j >= w[i]; --j){ //只考虑放的下的选择,
            dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
        }
    }
    printf("0 - 1 package\n");
    printf("the max value : %d\n", dp[n][m]);
    
    //列举选择的每个物品
    //1.先看该物品是不是被选择了,只需要看dp[i-1][j] == dp[i][j]
    //2.再退到该物品选择前的物品的dp值处
    int j = m;
    for(int i = n; i > 0; --i){
        if(dp[i - 1][j] != dp[i][j]){   //选择了该物品
            choice.push_back(i);    
            j -= w[i];  //回到上一个背包重量
        }
    }

    for(int i = choice.size() - 1; i >= 0; --i){
        printf("%d ", choice[i]);
    }
    
    
    return 0;
}

2. 完全背包

有 n 件物品和容量为 m 的背包,给出 n 件物品的重量 w[i] 以及价值 c[i] ,求解让装入背包的物品重量不超过背包容量且价值最大,每个物品可以重复选择 。

下面思路与 01 背包问题相似,仅在放第i件物品处存在差异。

  1. 不放第 i 件物品,问题转换为前 i - 1 件物品放入容量为 j 的背包中所能获得的最大价值,即 dp[i][j] = dp[i - 1][j] 。
  2. 放第 i 件物品,问题转换为前 i 件物品放入容量为 j - w[i] 的背包中所能获得的最大价值,即 dp[i][j] = dp[i][j - w[i]] 。
  3. 要求最大价值,则 dp[i][j] = max(dp[i - 1][j],dp[i][j - w[i]] + v[i])
  4. 求出背包中最终装的物品。
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;

const int MAX_N = 1e3;
const int MAX_M = 1e4;

int dp[MAX_N][MAX_M];  
int w[MAX_N];
int v[MAX_N];
vector<int> choice;
int n, m;

int main(){
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i){
        scanf("%d", &w[i]);
    }
    for(int i = 1; i <= n; ++i){
        scanf("%d", &v[i]);
    }
    
    //与0-1背包不同的是,每个物品可以任意多次,当选择第i件物品,dp[i][j] = dp[i][j - w[i]] + c[i]
    for(int i = 1; i <= n; ++i){
        for(int j = w[i]; j <= m; ++j){
            dp[i][j] = max(dp[i - 1][j], dp[i][j - w[i]] + v[i]);
        }
    }

    printf("\nentire package\n");
    printf("the max value : %d\n", dp[n][m]);

    j = m;
    for(int i = n; i > 0; --i){
        while(dp[i - 1][j] != dp[i][j]){   //选择了该物品
            choice.push_back(i);    
            j -= w[i];  //回到上一个背包重量
        }
    }

    for(int i = choice.size() - 1; i >= 0; --i){
        printf("%d ", choice[i]);
    }    

    return 0;
}

    上面背包问题求解在空间上还可以优化,因为每次的第 i 行只与第 i - 1 行的数据有关,可以去掉dp数组的第一维度,但是这样背包中选择的物品的序号就不能得到了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值