算法杂谈(一)之经典背包算法

596 阅读3分钟

这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战

动态规划

什么是动态规划?动态规划就是将一个大问题不断向下拆分成小问题,直到拆分出的小问题可以求出其解,然后将小问题的解不断的向上合并,最终得到大问题的解决方案。由于动态规划解决的问题多数有重叠子问题这个特点,为减少重复计算,对每一个子问题只解一次,将其不同阶段的不同状态保存在一个二维数组中

01背包

给定n种物品和一背包。物品 i 的重量似乎 wi,其价值为 vi,背包的容量为 c。
问应该如何选择装入背包中的物品,使得装入背包中物品的总价值最大?

简单思路

1.01背包问题与背包问题的区别在于,01背包,物品的选择只有两种一种是拿,另一种是不拿,而背包问题在于,物品可以只取一部分。所以01背包问题不能用贪心算法解决。

2.以dp[i][j]表示用i种物品,重量为j表示所取得的价值。

3.对于第i种物品,如果第i种物品重量大于j,就证明第i种物品肯定不能取,这时的dp[i][j]=dp[i-1][j]

4.如果第i种物品重量小于j,那就会出现两种情况,采用i的话,物品价值dp[i][j]=采用前面的i-1种物品,所占用的重量为j-i.getweight,所产生的价值+第i 种物品的价值。如果不采用i,价值为dp[i-1][j]。换成数学表达式就是dp[i][j]=Math.max(dp[i-1][j-weight]+value,dp[i-1][j]);

5.比如当i=5,j=10时,dp[5][10]就代表了所取得的最大价值。到这里我们就完成了任务的一半,接下为我们要寻找到底哪些物品放入了背包,从前面的表达式我们可以发现,当dp[i][j]=dp[i-1][j-weight]时,这时为i的物品就会放入背包,所以我们从结果,开始往回走,遇到这种情况,就说明有物品放入背包,然后物品数减1,重量减去为i的重量,继续,最后就能求出哪 些物品放入背包了。

参数

物品个数n = 3
背包容量c = 4
物品对应的价值数组 value[i] = [30,20,15]
物品对应的重量 weight[i] = [4,3,1]

构建表格

物品/容量1234
物品A(4KG)00030
物品B(3KG)002030
物品C(1KG)15152035

伪代码解析

1.初始化二维数组 
maxValue[n+1][c+1] //初始化数组时多构建一行一列,可防止数组越+代码的可读性

  2.网格构建 (主要是首行首列需要填充为0) 
       for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= c; j++) {
                maxValue[i][j] = 0;
            }
        }
    3.网格填充
            for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= c; j++) {
            	int topValue = maxValue[i-1][j];//取上一个网格的值 (即同一列上一行)
            	//先判断当前商品的重量是否大于背包容量,如果大于,则取上一个网格即topValue的值
            	//如果背包容量足以容纳当前物品的重量,1.取当前价值+剩余空间价值(容量-当前重量)
            	//若相等,则取当前价值
            	int thisValue = weight[i-1]<=j?
            						(j-weight[i-1]?value[i-1]+maxValue[i-1][j-weight[i-1]]:value[i-1])
            						:topValue;
            	//比对上一格,取最大值
            	max[i][j] = topValue>thisValue?topValue:thisValue;
            }
        }
   4.取数组最后一位则为最优