一文讲解01背包问题

本文将讲解动态规划中背包问题,常见有三类:

  • 0-1背包问题
  • 多重背包问题
  • 完全背包问题

上面三种都是背包问题,那么怎么区分呢?其实三种问题都很相似,解法也大体相同。

别急,先来区分上面三种,我们以问题描述来进行区分。

(1)0-1背包问题的描述

现在有四种物品,每种物品只有1件,它们的重量与价值如下表。

现在有一个背包,总容量为8。问怎么选取物品,可以使得背包装的物品价值最大?

物品编号

物品重量

物品价值

物品数量

1

2

3

1

2

3

4

1

3

4

5

1

4

5

8

1

(2)多重背包问题的描述

现在有四种物品,每种物品有若干件,它们的重量与价值如下表。

现在有一个背包,总容量为8。问怎么选取物品,可以使得背包装的物品价值最大?

物品编号

物品重量

物品价值

物品数量

1

2

3

2

2

3

4

2

3

4

5

2

4

5

8

2

(3)完全背包问题的描述

现在有四种物品,每种物品有无数件,它们的重量与价值如下表。

现在有一个背包,总容量为8。问怎么选取物品,可以使得背包装的物品价值最大?

物品编号

物品重量

物品价值

物品数量

1

2

3

无数件

2

3

4

无数件

3

4

5

无数件

4

5

8

无数件

通过以上例子,相信大家大概搞清楚了它们的区别吧。

0-1背包问题就是每种物品只有1件。

多重背包问题就是每种物品有若干件。

完全背包问题就是每种物品有无数件。

当然,我们讨论的背包问题对于每件物品而言,是不能够将该物品分割的,如果可以分割就要用到贪心算法了。

下面来讲解这三种背包问题的实现思路。

一、0-1背包问题

思路:对于每件物品,由于是不可分割的放入,所以,就有两种情况:该物品放入背包与该物品不放入背包;为了将以上问题求解出来,我们需要设置好状态以及状态转移方程。

  1. 定义状态

DP[k][w]:表示当背包剩余容量为w,现在有前k件物品可放的情况下,背包所能装物品的最大价值。

那么,状态确定好了,上面所描述的题目中,只要求出DP[4][8]就可以了。

等等,DP[4][8]是什么啊?DP[4][8]表示当背包剩余容量为8,现在有前4件物品可放的情况下,背包所能装物品的最大价值。

请问,这个DP[4][8]搞懂了没,搞懂了的话,我们继续看看DP[4][8]怎么求,那么也就是DP[k][w]怎么求的问题。

DP[k][w]怎么求呢,这就是状态转移方程的问题。

  1. 状态转移方程

我们先将状态转移方程写出来吧,就是:

DP[k][w] 等于下列两种情况:

①DP[k][w]=DP[k-1][w],当wk>w时

②DP[k][w]=max(DP[k-1][w],DP[k-1][w-wi]),当wk<=w时

解释:

第一种情况,当第k件物品的重量大于背包剩余的容量时,则DP[k][w]=DP[k-1][w];

这个关系式大家好好理解下,DP[k][w]表示的是当背包剩余容量为w,现在有前k件物品可放时,背包所能装物品的最大价值。由于第k件物品太重放不下背包,既然放不下第k件物品,就将其pass(排除)掉呗,相当于考虑放置前k-1件物品的情况,即f[k][w]=f[k-1][w]。

第二种情况,当第k件物品的重量不大于背包剩余的容量时,意味着第k件物品可以放置在背包中,“可以放置”并不意味着一定要放在背包中吧,所以,存在两种情形,第k件物品放在背包中或者第k件物品不放在背包中,这两种情形分别对应两种递推关系式,DP[k][w]=DP[k-1][w-wi]+v[i];(第k件物品放在背包中的情况,后面的v[i]是指第i件物品的价值,因为放进了背包,所以背包的价值多了v[i],同理,背包剩余容量就减少了wi

DP[k][w]=DP[k-1][w];(第k件物品不放在背包中的情况)

所以,第二种情况的结果就是取两种情形的最大值

DP[k][w]=max(DP[k-1][w-wi]+v[i],DP[k-1][w])

综合以上两种情况,DP[k][w]的值就是:

现在,我们要做的就是如何求出DP[4][8]的值,这个值的求解就是通过上面的状态转移方程进行推导,推导的过程就是填写以下表格。

首先,我们对以上表格进行初始化赋值,第0行的值都为0,DP[0][0]=0,DP[0][1]=0,DP[0][2]=0,DP[0][3]=0,…,DP[0][8]=0,为什么都是0呢,你想呀,因为是考虑前0件物品放进背包时的价值,0件物品的意思是说没有物品可放,既然没有物品可放,当然价值为0;

同理,第0列也都是为0,DP[0][0]=0,DP[1][0]=0,DP[2][0]=0,DP[3][0]=0,DP[4][0]=0,为什么都是0呢,因为背包剩余容量为0,说明背包装不下任何东西了,那么能装的价值当然为0;

以上就是赋初始值,将第0行与第0列都赋值为0,那么其它值该如何推导呢,我们也是根据状态转移方程进行推导,进而可以完成以下表格。

我们举个栗子(哈哈),看看图中红色方格中的数字怎么得到,红色方格代表DP[2][3],表示的是当背包剩余容量为3,现在有前2件物品可放时,背包所能装的最大价值。因为第2件物品的重量w2=3,此时背包剩余容量为3,说明第2件物品可以放置在背包中,所以,对应于状态转移方程中的第二种情况,DP[2][3]=max(DP[1][0]+4,DP[1][3])=4。

DP[2][3]能这样得到,那么其它方格中的数字,相信大家也一定能推导出来吧。不妨试试推导下DP[4][8]的值吧。

手撕代码,相信大家一定能看懂

#include<bits/stdc++.h>

using namespace std;

const int N=10010;

const int V=10010;

int dp[N][V];

int n,v;//n:物品数量,v:背包实际容量

int w[N];//第n件物品的重量

int c[N];//第n件物品的价值

void knap_01()

{

for(int i=1;i<=n;i++)

for(int j=1;j<=v;j++)

{

if(w[i]>j)

dp[i][j]=dp[i-1][j];

else

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

}

}

int main()

{

cin>>n>>v;

for(int i=1;i<=n;i++)

cin>>w[i];

for(int i=1;i<=n;i++)

cin>>c[i];

knap_01();

cout<<dp[n][v];

return 0;

}

时间复杂度:O(nv)

以上代码我们可以得出答案:DP[4][8]=12,如果我们要输出具体选择的方案,即具体选择了哪些物品呢?我们可以通过DP[4][8]往前进行回溯得到。

怎么回溯呢?再看如下表

从DP[4][8]进行反向推导,由于DP[3][8]的值等于9,如果背包中没有放入第4件物品,那么DP[4][8]的值一定等于DP[3][8]的值,而两者不相等,说明一定选择了第4件物品

既然选择了第4件物品,那么DP[4][8]是怎么得到呢,DP[4][8]是通过DP[3][3]+8得到。

DP[3][3]的值与DP[2][3]的值相等,说明没有选择第3件物品。

DP[2][3]的值与DP[1][3]的值不相等,说明选择了第2件物品

既然选择了第2件物品,那么DP[2][3]是怎么得到呢,DP[2][3]是通过DP[1][0]+4得到,DP[1][0]就是回溯的终点了,无需再进行回溯。

根据以上的过程,本题选择的物品编号分别为2号4号

以上过程可以通过递归进行解决,代码如下:

//0-1背包问题

//选择方案的输出

void print(int i,int j)

{

if(i==0||j==0) return;

if(dp[i][j]==dp[i-1][j])

print(i-1,j);

else

{

print(i-1,j-w[i]);

cout<<i<<" ";

}

}

以上就是0-1背包的思想,总结如下:

  1. 题目特点:每种物品只有1件,每次要么选择,要么不选择,就有原子性
  2. 定义状态

DP[k][w]:表示当背包剩余容量为w,现在有前k件物品可放的情况下,背包所能装物品的最大价值。

     3.状态转移方程

     4. 时间复杂度:O(nv)

     5. 方案的输出:使用回溯,运用递归,轻松输出


0-1背包问题我已经将视频分享到B站平台,引起了众多朋友的关注,如果有需要,大家可以前往B站平台观看:

https://www.bilibili.com/video/BV1g7411B7SP?share_source=copy_web&vd_source=d1f6364105c853f569fdfac1f2d6cb40


后续将继续推出多重背包,完全背包等经典问题,欢迎大家关注!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值