本来我是想解决公共最长子序列问题,但是在解题过程中需要先清楚动态规划算法,故先用动态规划算法中最经典的背包问题来说明。
背包问题
问题描述
有N件物品和一个容量为V的背包,第i件物品的费用是c[i],价值是w[i],求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大?
实际生活中的例子可以描述为小偷拿个袋子去偷东西,拿下图中的哪些东西装满背包才能获取最大价值?
这个问题的特点是:每种物品只有一件,可以选择放或者不放。
我们把题目具体下:
有3个商品,背包的体积为10,他们的体积为c[]={3,4,5}; 价值为 w={4,5,6}。V=10,N=3,c[]={3,4,5}, w={4,5,6}
从转移方程上可以看出,前i个物品的最优解只依赖于前i-1个物品最优解,而与前i-2,i-3,…各物品最优无直接关系,可以利用这个特点优化存储空间,即只申请一个一维数组即可,算法时间复杂度(O(VN))为:
for i=1..N
for v=V..0
f[v]=max{f[v],f[v-c[i]]+w[i]};
注意v的遍历顺序!!!
下面几种背包用到类似优化,以后不再赘述。
分析
在求最优解的背包问题中,一般有两种不同的问法:1、要求“恰好装满背包”时的最优解;2、求小于等于背包容量的最优解,即不一定恰好装满背包。
这两种问法,在初始化的时候是不同的。
(1)背包不一定装满
(2)背包一定装满
注意初始值,其中-inf表示负无穷:
这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。可以将背包问题的求解看作是进行一系列的决策过程,即决定哪些物品应该放入背包,哪些不放入背包。
如果一个问题的最优解包含了物品n,即Xn = 1,那么其余X1, X2, …..,Xn-1
一定构成子问题1,2,…..,n-1在容量C - cn时的最优解。如果这个最优解不包含物品n,即Xn = 0;
那么其余 X1, X2…..Xn-1一定构成了子问题 1,2,….n-1在容量C时的最优解。 //请各位仔细品味这几句话
根据以上分析最优解的结构递归定义问题的最优解: f[i][v] = max{ f[i-1][v] , f[i-1][v - c[i]] + v[i]}
编码
import java.io.IOException;
/**
* @param
* @Title: 动态规划-背包问题
* @Author: xuming
* @Description:
* @date:2016/5/31 9:22
* @return
*/
public class myMathDemo2 {
public static void main(String[] args) throws IOException {
int c[] = {0, 3, 4, 5};
int v[] = {0, 4, 5, 6};
int f[][] = new int[4][11];
for (int i = 1; i < 4; i++)
for (int j = 1; j < 11; j++) {
if (c[i] > j)//如果背包的容量,放不下c[i],则不选c[i]
f[i][j] = f[i - 1][j];
else {
f[i][j] = Math.max(f[i - 1][j], f[i - 1][j - c[i]] + v[i]);//转移方程式
System.out.println(f[i][j]);
}
}
System.out.println("last answer: " + f[3][10]);
for (int m = 0; m < 4; m++) {
for (int n = 0; n < 11; n++) {
System.out.println("the matrix answer f[" + m + "][" + n + "]:" + f[m][n]);
}
}
}
}
结果是: