这段时间天天加班,确实没有整块的时间来写博客了,一不当心就到周末了,要是不写篇博客,那就又要鸽了。为了避免打脸,仍是加班加点的把这篇博客给写了出来。java
再说个题外话,最近一直在看一本关于Mysql
的掘金小册,感受很棒,做者用通俗易懂的语言将Mysql
的底层原理进行了介绍,图文并茂,讲解的很深刻,能够看出做者应该是花了很多心思,借阅了很多书籍的。听说做者是个95后,为了写这本小册子还特地辞了职,简直优秀!sql
一篇文章大概须要花费40~60分钟,建议花整块的时间进行阅读。数组
从做者的身上,也看到了一种匠心精神,反观本身,写这么水的文章,实在是惭愧。因此决定对文章质量把把关,本着宁缺毋滥的原则来写做,尽可能不浪费你们的时间。bash
好了,闲话就说到这了,言归正传。优化
上一篇中,咱们了解了01背包问题
,并用三种方法进行了求解,但其实在最后一种解法上,咱们还能再对它的空间复杂度进行优化。spa
已通过去一个星期了,可能一部分人已经忘记了以前的解题思路,因此在这里把以前填表法使用到的图贴了过来:3d
这是咱们上一篇填表法的最终结果,在这里,聪明的你应该能发现,其实这里大部分的内容都没有用上,那么让咱们来想一想,如何优化一下空间复杂度呢?code
再回头看下以前的递推关系式:cdn
能够发现,每次求解 KS(i,j)
只与KS(i-1,m) {m:1...j}
有关。也就是说,若是咱们知道了K(i-1,1...j)
就确定能求出KS(i,j)
,为了更直观的理解,再画一张图:blog
下一层只须要根据上一层的结果便可推出答案,举个栗子,看i=3,j=5
时,在求这个子问题的最优解时,根据上述推导公式,KS(3,5) = max{KS(2,5)
,KS(2,0) + 3} = max{6,3} = 6
;若是咱们获得了i=2
时全部子问题的解,那么就很容易求出i=3
时全部子问题的解。
所以,咱们能够将求解空间进行优化,将二维数组压缩成一维数组,此时,装填转移方程变为:
KS(j) = max{KS(j),KS(j - wi) + vi}
复制代码
这里KS(j - wi)
就至关于原来的KS(i-1, j - wi)
。须要注意的是,因为KS(j)
是由它前面的KS(m){m:1..j}
推导出来的,因此在第二轮循环扫描的时候应该由后往前进行计算,由于若是由前日后推导的话,前一次循环保存下来的值可能会被修改,从而形成错误。
这么说也许仍是不太清楚,回头看上面的图,咱们从i=2
推算i=3
的子问题的解时,此时一维数组中存放的是{0,0,2,4,4,6,6,6,6,6,6}
,这是i=2
时全部子问题的解,若是咱们从前日后推算i=3
时的解,好比,咱们计算KS(0) = 0,KS(1) = KS(1) = 0
(由于j=1时,装不下第三个珠宝,第三个珠宝的重量为5),KS(2) = 2,KS(3) = 4,KS(4) = 4, KS(5) = max{KS(5), KS(5-5) + 3} = 6,....,KS(8) = max{KS(8),KS(8 - 5) + 3} = 7
。在这里计算KS(8)的时候,咱们就把原来KS(8)的内容修改掉了,这样,咱们后续计算就没法找到这个位置的原值(这个栗子没举好。。由于后面的计算没有用到KS(8)= =),也就是上一轮循环中计算出来的值了,因此在遍历的时候,须要从后往前进行倒序遍历。
public class Solution{
int[] vs = {0,2,4,3,7};
int[] ws = {0,2,3,5,5};
int[] newResults = new int[11];
@Test
public void test() {
int result = ksp(4,10);
System.out.println(result);
}
private int ksp(int i, int c){
// 开始填表
for (int m = 0; m < vs.length; m++){
int w = ws[m];
int v = vs[m];
for (int n = c; n >= w; n--){
newResults[n] = Math.max(newResults[n] , newResults[n - w] + v);
}
// 能够在这里输出中间结果
System.out.println(JSON.toJSONString(newResults));
}
return newResults[newResults.length - 1];
}
}
复制代码
输出以下:
[0,0,0,0,0,0,0,0,0,0,0]
[0,0,2,2,2,2,2,2,2,2,2]
[0,0,2,4,4,6,6,6,6,6,6]
[0,0,2,4,4,6,6,6,7,7,9]
[0,0,2,4,4,7,7,9,11,11,13]
13
复制代码
这样,咱们就顺利将空间复杂度从O(n*c)
优化到了O(c)
。固然,空间优化的代价是,咱们只能知道最终的结果,但没法再回溯中间的选择,也就是没法根据最终结果来找到咱们要选的物品组合。
01背包问题
通常有两种不一样的问法,一种是“刚好装满背包”
的最优解,要求背包必须装满,那么在初始化的时候,除了KS(0)
为0
,其余的KS(j)
都应该设置为负无穷大
,这样就能够保证最终获得的KS(c)
是刚好装满背包的最优解。另外一种问法不要求装满
,而是只但愿最终获得的价值尽量大
,那么初始化的时候,应该将KS(0...c)
所有设置为0
。
为何呢?由于初始化的数组,其实是在没有任何物品能够放入背包的状况下的合法状态
。若是要求背包刚好装满,那么此时只有容量为0的背包能够在什么都不装且价值为0的状况下被“刚好装满”
,其余容量的背包均没有合法的解,所以属于未定义的状态,应该设置为负无穷大
。若是背包不须要被装满,那么任何容量的背包都有合法解,那就是“什么都不装”。这个解的价值为0,因此初始状态的值都是0。
01背包问题
能够用自上而下
的递归记忆法
求解,也能够用自下而上
的填表法
求解,然后者能够将二维数组的解空间优化成一维数组的解空间,从而实现空间复杂度的优化。
对于01背包问题
的两种不一样问法,实际上的区别即是初始值
设置不同,解题思路是同样的。
关于01背包问题
,介绍到这里就已经所有结束了,但愿能对你们有所帮助。若是以为有收获,不要吝啬你的赞哦,也欢迎关注个人公众号留言交流。