动态规划(Dynamic Programming) 背包问题初探

引例:
如何计算斐波那契数列?

  • 递归
  • 记忆化搜索
  • 动态规划(递推)
    动态规划:避免无用的重复计算,提高处理的效率。
    将一个问题拆成几个子问题,分别求解这些子问题,即可推断出大问题的解。当子问题具有重叠性时 (同一个子问题多次出现),相比于搜索 (递归),利用 dp 就可以避免重复计算,节省时间。
  • 适用条件
    最优子结构:大问题的最优解可以由小问题的最优解推得。
    无后效性:当前问题的解只与已求得问题 (过去) 的解有关,而与未求得的问题 (未来) 的解无关

解决方法:

  • 确定状态
    状态是比较抽象的,如何正确的定义状态需要多做题,不同类型
    的 dp 有各自的套路。
  • 确定转移方程
    当前状态可以影响后续的哪些状态or当前状态可以被之前的哪些状态影响。

最为经典的背包DP

  • 01 问题详情
    有 n 种物品和一个容量为 m 的背包。每种物品只有一件。第 i种物品的大小是 wi,价值是 vi,求如何使装入背包中物品的价值最大?
    B站视频讲解
    这个极其清楚
  • 02 解题思路
    确定状态:f[i][j]:只选择前 i 种物品,大小总和为 j 时的最大价值。
    转移方程:
    f[i][j] = max(f[i − 1][j], f[i − 1][j − w[i]] + v[i])
    参数解释:f[i − 1][j] 不装第i件物品的价值。
    f[i − 1][j − w[i]] + v[i] 装了第i件物品(价值增加,容量减少)
    注意:应该是先从第1件物品向下推。
    源数据数组展示
    示例代码:
#include <iostream>
//Use C to make a knapsack
#define N 6
#define W 21
int B[N][W]={0};
int w[6]={0,2,3,4,5,9};
int v[6]={0,3,4,5,8,10};
//w:容量
//v:对应的价值
//数组0的地方不用方便读取
using namespace std;
void knapsack(){
    int k,C;
    //c是当前剩余容量
    //k是前多少件物品的最大价值
    //当然是从第一件物品往里面放的话更顺一点
    for (k = 1; k < N; ++k) {
        for (C = 1; C < W; ++C) {
            if(w[k]>C){
                //如果当前背包放不进去,那这件物品就不放进去了
                B[k][C]=B[k-1][C];
            } else{
                int value1=B[k-1][C-w[k]]+v[k];
                //是买第K件商品,然后剩下的空间去买k前面的商品的总价值高
                int value2=B[k-1][C];
                //还是不买第k件商品的总价值高
                //这个相当于不放第K件商品进去
                //比较哪个最大价值更大
                if(value1>value2){
                    B[k][C] = value1;
                }else{
                    B[k][C] = value2;
                }
            }
        }
    }
}
int main() {
    knapsack();
    cout<<B[5][20]<<endl;
    return 0;
}

多重背包 (每个物品有k件)
多重背包转移方程

完全背包(物品变成了无数件)
转移方程
完全背包转移方程
由于背包容量有限,每种物品选取的个数是有限的,所以可以转化为多重背包。
完全背包的最暴力代码:
完全背包的最暴力代码
同理,多重背包的第三次循环判断条件不是容量,是在满足容量要求的同时,满足数量的要求。改变第三次循环的条件即可:
多重背包的暴力解法
需要注意的是,以上代码并没有进行优化,均为背包问题的最简单解法,常用的优化方法如下:

01背包多重背包完全背包
滚动数组、一维数组滚动数组、一维数组,二进制优化一维数组

多重背包和完全背包参考:
cqh小蒟蒻 背包讲解
背包九讲貌似是一个很不错的资料,可以看看。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值