背包问题
看了一天终于看懂了大概,赶紧记下来,为方便同小白和我自己再忘了的时候理解。全网最傻瓜版,不会吧不会真的有人看这个看不懂吧
起因
来源于一到题目,找零钱。
coins=[1,2,5],amount = 11,求最少需要的硬币数。
对于没接触过这类问题的我:
第一思路,贪心,挨个求余数。
余数的问题是顺序会产生影响。比如硬币5,3,4对总数11。贪心的先用5求余数,发现余1,GG。必须从3或者4开始算才能把结果求出来。如果硬币池子更大,原地爆炸。
第二思路,暴力枚举,反正都是整数,硬币又不能掰开花。但想都不用想池子大了这复杂度,还是算了。暴力是小白永远的终极一换一杀手锏。
那么有没有一款经济实惠,简单易懂的算法呢?(算法本身简单易懂或存疑,但我说的应该够简单粗暴了,希望我再看到自己的这个博客不会骂现在的自己写的什么东西这么不清楚
背包问题
怎么把价值分别为x,y,z,重量分别为X,Y,Z的物品,装在总容量为C的包里?
可重复情况(完全背包问题)
我是先看懂这个的,可能和别人科班的顺序不一样。
先说本质,不管完全不完全,本质上都是要找状态的变化。
核心的公式为
F(X) = max{ F(i-w)+v,F(X-1)}
意思就是,我这次是怎么样的要通过上一状态来算,也就是找递归。
现在的包容量为X,价值为Y,我有两种选择,装新的货物或者不装。
装重W价值V的新货物的话:
装之前:重量= X-W,价值为F(X-W)
装之后:重量= X,价值为F(X-W)+V
如果不装:装之前之后不变就是F(X) = F(X-1)
哪个符合要求用哪种,最大最小都行的。
这里注意不是说在F(X)上加,是在上一状态加,不然这个就没有比较价格的必要了。
举例:货物重量1,2,5,价值2,3,5。
一个一个来:
F1=2,就这么大空,只能放下1。
F2= MAX{F1,F(1+1),F(0+2)},三种选择,不加,在1的基础上加1,在零的基础上加2。
F3………………
F4………………
F5=MAX{F4,F(3+2),F(4+1),F(0+5)},不加,在3的基础上加2,在4的基础上加1,在0的基础上加5。往后都一样了,最多就货物种类+1种可能性。
我每步都是最优,加起来也是。
第一步:看看有没有空。第二步:加上去看看哪个好。
用python程序写的话:
# W是重量 V是价格
for i in range(1, rongliang + 1):
y = [int(0)] * len(W)
for index, w in enumerate(W):
if i - w >= 0:
y[index] = value[i - w] + V[index]
else:
y[index] = 0
dp[i] = max(y)
当然了这个代码写的很糟,不过意思到了,这样就可以完成完全背包问题了。
回到硬币
硬币和这个不一样的就是货物希望越贵越好,硬币希望的是越少越好,min就好了。单价都是1的货物,面值是重量,总价是容量。
0-1背包问题
0-1就是有或无。和之前比出现了一个需要考虑不能重复的问题。
还是看例子:
货物重量 W = [5, 3, 6, 4, 2]
货物价格 V = [5, 2, 8, 9, 3] 都是绝世孤本,求我就一个7大小的包,怎么装最赚。
这个问题最重要的是画个表。
V W 0 1 2 3 4 5 6 7
0 0 [ 0 0 0 0 0 0 0 0]
5 5 [ 0 0 0 0 0 5 5 5]
2 3 [ 0 0 0 2 2 5 5 5]
8 6 [ 0 0 0 2 2 5 8 8]
9 4 [ 0 0 0 2 9 9 9 11]
3 2 [ 0 0 3 3 9 9 12 12]
行为货物,列为包容量,值为价格,这个表就是这个问题的精髓,如何填表。
以一列一列来填为例子,第一行第一列都是零,没东西。第二列第二行,看看是个5,容量是1,塞不进去,直到2,才能塞进去,2那行就是3。
第三列第二行,还是塞不进去,但是3能塞进去了,所以写个2,6,4都不行,2还可以,价格也比上面的好,最后一行是3。
第五列,终于能塞进去了写个5,第三行,塞进去的话是个2,不如5,依然写五,直到4塞进去是个9,改成9。
所以就是从上往下一个一个塞,大了再改。一个格子只要有空可以都塞进去。
那么每次塞进去的时候需要考虑:
第一步:能不能进去 J>=W,如果塞不进去那就用上一列的。
第二步:进去之后合不合适,max{F(上一行),F(塞进去之后)}。第三步:把冰箱门关上。
这里要注意的是,5虽然是第一个塞进去的,但是不是不和别人比,是和第一行的0比,这就是第一行存在的意义。
那么程序:
for i in range(1, hang + 1):#行
for j in range(1, lie + 1):#列
if j >= W[i - 1]:
value[i][j] = max(value[i - 1][j - W[i - 1]] + V[i - 1], value[i - 1][j])#比一比
else: value[i][j]=value[i-1][j]#塞不进去那就接着用之前的
print(value)
搞定,还有个多背包,有空再说。