码迷,mamicode.com
首页 > 其他好文 > 详细

一文解决背包问题(上)

时间:2020-12-19 13:29:18      阅读:2      评论:0      收藏:0      [点我收藏+]

标签:code   nlp   不可   需要   链接   10个   两种   状态转移方程   多个   

微信公众号:Jerry的算法和NLP

技术图片

| 背包问题主要分为以下几类

0/1背包问题
完全背包问题
多重背包问题
多重背包问题的优化
混合背包问题
二维背包问题
分组背包问题
有依赖的背包问题

背景

首先说下背包问题的背景
背包问题一般都是 主要会提供这个背包的容量V
然后给出一系列的物品
物品主要以下几个属性
价值V 体积W 物品个数S
然后求在这么多物品中求出在背包容量下能够放置物品的最大价值


首先先定义一个DP数组 DP[i][j]
i—为第i个物品
j—为容量为j

动态规划的题目
主要是列出状态转移方程
让计算机去帮助你完成各种运算

例子

dp[3][4]代表着当前遍历到第3个物品且此时背包的容量为4
然后假设当前第三个物品的价值为2 体积为1
那么dp[3][4]有两种情况

一种就是当前选择这个物品
那么在第二个物品时候你就得选择容量为4-1=3即
dp[3][4]=dp[2][3]+当前物品的价值2
或者不选当前物品 dp[3][4]=dp[2][4]

另一种就是不选择当前这个物品
那么dp[3][4]=dp[2][4]

假设现在选择当前物品

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

假设现在不选择当前物品

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

0/1背包问题

技术图片
0/1背包问题下
物品个数为1
其他条件同通用条件相同

题目链接:
https://www.acwing.com/problem/content/2/

假设现在选择当前物品

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

假设现在不选择当前物品

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

伪代码

1for i in range(1,N+1):  #遍历物品
2   for j in range(V+1):     #遍历容量   
3       if j>=w[i]:   #可以放入物品     
4           dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])     
5       else:        #不可以放入物品
6          dp[i][j]=dp[i-1][j]

完全背包问题

技术图片
题目链接:
https://www.acwing.com/problem/content/3/

完全背包问题就是可以选择无限次物品
0/1中就是只有选择当前物品或者不选择当前物品

完全背包中添加了一个选择多少个物品的状态在里面
所以我们初始化dp[i][j]
i—为第i个物品
j—为容量为j

但是在两层循环中再加一个对物品个数的轮询
而且在表达式上有所改变

还是两个状态 在这个容量下 能不能选当前物品

  • 能选的情况下 要不要选
    能选的情况下
    不同0/1背包 继承上一个物品的状态dp[i-1][j]并且做出的选择
1dp[i-1][j-w[i]]+v[i]

完全背包下继承的是 选择当前物品后的状态 也就是说这个状态下我们不知道选择了多少物品,但是只要有最优解那么选上就是了

1dp[i][j]=max(dp[i][j-w[i]]+v[i],dp[i-1][j])
  • 不能选的时候直接继承上一个状态
1dp[i][j]=dp[i-1][j]

伪代码

1for i in range(1,N+1):
2    for j in range(V+1):
3        if j>=w[i]:
4            dp[i][j]=max(dp[i][j-w[i]]+v[i],dp[i-1][j])
5        else:
6            dp[i][j]=max(dp[i-1][j],dp[i][j])

多重背包问题

技术图片

https://www.acwing.com/problem/content/4/
多重背包问题是物品个数为有限个的问题

对于这个物品,有选择0,1,2,3,...,n个的状态,那么就是我们需要轮询物品的状态

读到这里可能会有人问 为什么在完全背包中不用轮询物品的状态呢
因为完全背包中物品的个数是无限个 无法进行轮询
所以我们在状态转移的时候把转移基于的状态从上一个物品变成基于当前物品,这样就可以达到无限多个物品的状态
遍历的过程中判断当前物品个数*体积是否能够塞进背包里

1.K个物品可以放到容量为J的背包中 那么既可以选 也可以不选

1dp[i][j]=max(dp[i-1][j-w[i]*k]+v[i]*k,dp[i][j])

2.K个物品放不到容量为J的背包中 那么就选不了 直接继承

1dp[i][j]=max(dp[i-1][j],dp[i][j])

伪代码

1for i in range(1,N+1):
2    for j in range(V+1):
3        for k in range(s[i]+1):
4            if j>=k*w[i]:
5                dp[i][j]=max(dp[i-1][j-w[i]*k]+v[i]*k,dp[i][j])
6            else:
7                dp[i][j]=max(dp[i-1][j],dp[i][j])

多重背包问题 二进制优化

技术图片

题目链接:
https://www.acwing.com/problem/content/5/
上一个多重背包问题 体积、物品和物品个数范围都在10^2以内,那么三层for循环的复杂度为10^6 是在可以接受的范围内

这个题目 体积、物品和物品个数范围都在10^3以内 如果说按照三层for循环的复杂度的话那么就是10^9
所以我们需要进行复杂度方面的优化

首先我们的物品个数是用十进制的方式进行表达的,那么我们可以首先将十进制的物品转换为若干个二进制的物品。

具体怎么转换?我来举个例子你就懂了。
假设物品A是有10个,那么我将它进行拆分成1+2+4+3个
如果我要选择3个A,那么我的选择就是1+2
如果我要选择5个A,那么我的选择就是2+3
这样的话物品的个数就可以由若干个转换成二进制的物品表示了

然后有限个物品就可以转换成0/1背包问题进行解决了
还是两个状态 当前物品能不能放入

1.能放入 想不想放入

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

2.不能放入 直接继承

1dp[i][j]=max(dp[i-1][j],dp[i][j])

伪代码

1for i in range(1,len(w)):
2    for j in range(1,V+1):
3        if j>=w[i]:
4            dp[i][j]=max(dp[i-1][j-w[i]]+v[i],dp[i-1][j],dp[i][j])
5        else:
6            dp[i][j]=max(dp[i-1][j],dp[i][j])

小结

说到底还是状态转移方程的问题,明确当前状态下有多少种可能

只有选或者不选这个物品 两种可能

  • 选这个物品
    那么基于上一个物品(0/1背包)还是基于当前物品的之前的决策(完全背包问题)

  • 不选这个物品 那么直接继承

技术图片
计算机解决问题无非穷举而已

一文解决背包问题(上)

标签:code   nlp   不可   需要   链接   10个   两种   状态转移方程   多个   

原文地址:https://blog.51cto.com/15054042/2564171

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!