01背包问题

目录

一、前言

二、01背包

1、问题描述

2、基本思路

3、优化空间复杂度

三、题例

1、上链接

2、题目分析

3、代码

(1)C++

(2)python——二维朴素写法

(3)python——一维空间优化写法


一、前言

对于学计算机的同学来说,学习算法是一件非常重要的事情,废话不多讲,我们来讲讲“01背包问题”。

二、01背包

何谓01背包,对于每个要放入背包的物品,只需要考虑放与不放两种选择。如果不选择将其放入背包中,则不需要处理,视为0,反之视为1。

1、问题描述

有N件物品和一个容量为V的背包。放入第 i 件物品耗费的费用(体积)是 Ci,得到的价值是 Wi。求:将哪些物品装入背包可使价值总和最大。

2、基本思路

​01背包是最基础的背包问题,特点是:每种物品只有一件,可以选择放或者不放。

我们第一反应要想到用子问题来定义状态:即 dp[i][v] 表示前 i 件物品放入一个容量为 v 的背包中可以获得的最大价值。其状态转移方程为:

dp[i][v] = max(dp[i - 1][v], dp[i - 1][v - c[i]] + w[i]);

该方程理解起来非常简单,来解释一下,实际上直接看方程也能大致知道想表达什么。

“将前 i 件物品放入容量为 v 的背包中”这个子问题,若只考虑第 i 件物品的策略(放或不放),那么就可以转化为一个只和前 i - 1 件物品相关的问题。如果不放第 i 件物品,那么问题就转化为“前 i - 1 件物品放入容量为 v 的背包中”,价值为 dp[i- 1][v];如果放第 i 件物品,那么问题就转化为“前 i − 1 件物品放入剩下的容量为 v − Ci 的背包中”,此时能获得的最大价值就是 dp[i − 1][v − Ci] 再加上通过放入第 i 件物品获得的价值 Wi。

​据说这个方程非常重要,基本上所有跟背包相关的问题的方程都可以由它衍生出来。

最后稍微思考一下可得到递推式逻辑:

(不能装)如果 当前背包容量V[i] < 待装物品体积C[i] ,则 dp[i][v]=dp[i-1][v] 

(能装,决策装不装)否则 dp[i][v] = max(dp[i - 1][v], dp[i - 1][v - c[i]] + w[i])

补充:动态规划法与分治法的区别

动态规划与分治法类似,都是把大问题拆分成小问题,通过寻找大问题与小问题的递推关系,解决一个个小问题,最终达到解决原问题的效果。但不同的是,分治法在子问题和子子问题等上被重复计算了很多次,而动态规划则具有记忆性,通过填写表把所有已经解决的子问题答案记录下来,在新问题里需要用到的子问题可以直接提取,避免了重复计算,从而节约了时间,所以在问题满足最优性原理之后,用动态规划解决问题的核心就在于填表,表填写完毕,最优解也就找到。

3、优化空间复杂度

本文着重让读者明白何谓01背包以及解01背包的基本算法思路,将空间二维优化为空间一维需要更好的基础,写了可能读者在比赛或机考时不一定能用得上,综合考虑后决定还是不写,感兴趣的兄弟可以找其他博客阅读阅读,或者到下面例题的题解里面搜寻想要的解释。

三、题例

1、上链接

2. 01背包问题 - AcWing题库

2、题目分析

这是一道模板题,直接套模板即可。

3、代码

(1)C++

#include <iostream>
using namespace std;

const int MAXN=1003;
int V[MAXN];    //每个背包的体积
int W[MAXN];    //每个背包的价值
int dp[MAXN][MAXN];    //dp[i][j], j体积下前i个物品的最大价值

int main() {
    int n,v;
    cin>>n>>v;

    for(int i=1;i<=n;++i)
        cin>>V[i]>>W[i];

    for(int i=1;i<=n;++i){       //该层循环为了遍历每个背包
        for(int j=1;j<=v;++j){      //为了填表中的每一行,得到在背包任意体积下的最优解,填表见下图
            if(j<V[i])
                dp[i][j]=dp[i-1][j];
            else
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-V[i]]+W[i]);
        }
    }

    cout<<dp[n][v]<<endl;
    return 0;
}
/* 补充:
 * 当前的状态依赖于之前的状态,我们可以默认为从初始状态(边界) dp[0][j] = dp[i][0] = 0开始决策,
 * 有 N 件物品,则需要 N 次决策,每一次对第 i 件物品的决策,状态dp[i][j]不断由之前的状态更新而来。
 * 显然,当只有 0 件物品的时候,或者背包最大容量为 0 的时候, dp[0][j] = dp[i][0] = 0
 * */

(2)python——二维朴素写法

MAXN=1003
dp=[[0]*MAXN for i in range(MAXN+1)] #注意创建二维数组是这样子
V=[0]*MAXN
W=[0]*MAXN

# test=[[0]*MAXN]*MAXN   #这样创数组有错误
# print(test[10][10])

n,v=map(int,input().split())

for i in range(1,n+1):
    V[i],W[i]=map(int,input().split())

for i in range(1,n+1):
    for j in range(1,v+1):
        if j<V[i]:
            dp[i][j]=dp[i-1][j]
        else:
            dp[i][j]=max(dp[i-1][j],dp[i-1][j-V[i]]+W[i])

print(dp[n][v])

(3)python——一维空间优化写法

MAXN=1003
dp=[0]*MAXN
V=[0]*MAXN
W=[0]*MAXN

n,v=map(int,input().split())

for i in range(1,n+1):
    V[i],W[i]=map(int,input().split())

for i in range(1,n+1):
    for j in range(v,0,-1):
        if j<V[i]:
            dp[j]=dp[j]
        else:
            dp[j]=max(dp[j],dp[j-V[i]]+W[i])

print(dp[v])

# dp=[0,1,2,3,4,5,6,7,8,9,10,11,21]
# for i in range(12,-1,-1):   #这样是逆序输出全部元素
#     print(dp[i],end=" ")

一维为何要这样写呢?先上一张图。

 懂的人自然懂,不懂的人仔细思考一下也会懂~

以上,01背包,更多内容可到Acwing讨论区学习。

祝好,加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吕飞雨的头发不能秃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值