DP:0-1背包,完全背包,多重背包

学习自 https://www.bilibili.com/video/av70148899?p=3 ,讲得很清晰

背包问题

给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高

一丶0-1背包

我们有 n n 种物品,物品 i i 的重量为 W i W_i ,价值为 V i V_i ,我们假定所有物品的重量和价值都是非负的。如果限定每种物品只能选择0个或1个,求背包所能承受的最大重量为 W W 在这里插入图片描述
v i w i d p [ i ] [ j ] i j v_i:价值 , w_i:重量 \\ dp[i][j]:处理了前i件商品,背包可容纳重量为j的情况下的最优装配方案
d p [ i ] [ j ] = max ( d p [ i 1 ] [ j ] , d p [ i 1 ] [ j w i ] + v i ) dp[i][j]=\max (dp[i-1][j],dp[i-1][j-w_i]+v_i)

空间优化:

上面的空间复杂度是O(nW),假如我们只需要用最后的结果,那么我们可以将空间复杂度压缩到O(n),因为01背包的问题可以正确更新依赖于上一行 j w i j-w_i j j
在这里插入图片描述
wi之前肯定没有更新,因为这段是没有必要去更新的
深绿色的之所以安全:因为wi之前的没有被更新是上一行的内容;
红色的不安全:因为用到的深绿色的内容是本行的内容,所以一旦深绿色的出现变换,那么红色就会更新错误
解决方案:从右到左更新

const int Wsize = 5005; 
int dp[Wsize];
int W; //背包总承受重量
int v[M]; int w[M]; //价值,重量
void init() {
    memset(dp, 0, sizeof(dp));
}
void zero_one_bag(int vi,int wi) {
    for (int i = W; i >= wi; i--)
        dp[i] = max(dp[i], dp[i - wi] + vi);
}
int solve(int n) {  //n是物品数量
    for (int i = 0; i < n; i++) {
        zero_one_bag(v[i], w[i]);        
    }
   return  dp[W];
}

放满情况:
初始化: memset(dp, -1, sizeof(dp));,-1代表这种方案不可行
且dp[0]=0;

二丶完全背包

我们有 n n 种物品,物品 i i 的重量为 W i W_i ,价值为 V i V_i ,我们假定所有物品的重量和价值都是非负的。如果限定每种物品只能选择无限个,求背包所能承受的最大重量为 W W
在这里插入图片描述

上面的思路是将完全背包看成01背包,思路就是每个商品拥有不能再装的数量,比如1号商品拥有3个,2号商品拥有2个,最后变为5个商品的01背包问题,但我们没有用到他们是同一件商品的特性, 一旦我们用了这个特性,直接从左往右更新,那么我们可以让时间复杂度降低,避免了同个物品的循环
在这里插入图片描述

const int Wsize = 5005; 
int dp[Wsize];
int W; //背包总承受重量
int v[M]; int w[M]; //价值,重量
void init() {
    memset(dp, 0, sizeof(dp));
}
void complete_bag(int vi, int wi) {
    for (int i =wi; i <= W; i--)
        dp[i] = max(dp[i], dp[i - wi] + vi);
}
int solve(int n) {  //n是物品数量
    for (int i = 0; i < n; i++) {
        complete_bag(v[i], w[i]);        
    }
   return  dp[W];
}

三丶多重背包

我们有 n n 种物品,物品 i i 的重量为 W i W_i ,价值为 V i V_i ,我们假定所有物品的重量和价值都是非负的。如果限定每种物品只能选择有限个,求背包所能承受的最大重量为 W W

下面是位运算来写
n = 1 + 2 + 4 + + n   13 = 1 + 2 + 4 + 6 n=1+2+4+\cdots+n-\sum前面的数\\比如 \ 13=1+2+4+6
比如   13 = 1 + 2 + 4 + 6 \ 13个物品=1+2+4+6 ,那么就可以转化为01背包,不过1 ,2 ,4 ,6都要化为整体,乘以参数vi * k, wi * k
证明需要去看看数学的了

const int Wsize = 5005; 
int dp[Wsize];
int W; //背包总承受重量
int v[M]; int w[M]; int c[M]; //价值,重量,数量
void init() {
    memset(dp, 0, sizeof(dp));
}
void muti_bag(int vi, int wi, int ci) {
	//数量足够,直接完全背包做,也可以不必要写
    if (W / wi <= ci) {
        complete_bag(vi, wi);
        return;
    }

    // 1~13 :1 2  4 + 6   
    for (int i = 1; i <= ci; i <<= 1) {
        ci -= i;
        zero_one_bag(vi * i, wi * i);
    }
    zero_one_bag(vi * ci, wi * ci);
}
int solve(int n) {  //n是物品数量
    for (int i = 0; i < n; i++) {
        muti_bag(v[i], w[i], c[i]);        
    }
   return  dp[W];
}
发布了178 篇原创文章 · 获赞 140 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_42146775/article/details/104911223
今日推荐