1 相关概念:
动态规划是一种在数学和计算机科学中使用的,用于求解包含重叠子问题的最优化问题的方法。其基本思想是,将原问题分解为相似的子问题,在求解的过程中通过子问题的解求出原问题的解。动态规划的思想是多种算法的基础,被广泛应用于计算机科学和工程领域。比较著名的应用实例有:求解最短路径问题,背包问题,项目管理,网络流优化等。
2 相关特性:
动态规划是用空间换时间的一种方法的抽象。动态规划在查找有很多重叠子问题的情况的最优解时有效。它将问题重新组合成子问题。为了避免多次解决这些子问题,它们的结果都逐渐被计算并被保存,从简单的问题直到整个问题都被解决。因此,动态规划保存递归时的结果,因而不会在解决同样的问题时花费时间。
动态规划只能应用于有最优子结构的问题。最优子结构的意思是局部最优解能决定全局最优解(对有些问题这个要求并不能完全满足,故有时需要引入一定的近似)。简单地说,问题能够分解成子问题来解决。
3 动态规划解决背包问题:
3.1问题说明:
/* 一个旅行者有一个最多能用M公斤的背包,现在有N件物品,
它们的重量分别是W1,W2,...,Wn,
它们的价值分别为P1,P2,...,Pn.
若每种物品只有一件求旅行者能获得最大总价值。
输入格式:
M,N
W1,P1
W2,P2
......
输出格式:
X
*/
3.2问题分析:
因为背包最大容量M未知。所以,我们的程序要从1到M一个一个的试。比如,开始任选N件物品的一个。看对应M的背包,能不能放进去,如果能放进去,并且还有多的空间,则,多出来的空间里能放N-1物品中的最大价值。怎么能保证总选择是最大价值呢?看下表。
测试数据:
10,3
3,4
4,5
5,6
c[i][j]数组保存了1,2,3号物品依次选择后的最大价值.
这个最大价值是怎么得来的呢?从背包容量为0开始,1号物品先试,0,1,2,的容量都不能放.所以置0,背包容量为3则里面放4.这样,这一排背包容量为4,5,6,....10的时候,最佳方案都是放4.假如1号物品放入背包.则再看2号物品.当背包容量为3的时候,最佳方案还是上一排的最价方案c为4.而背包容量为5的时候,则最佳方案为自己的重量5.背包容量为7的时候,很显然是5加上一个值了。加谁??很显然是7-4=3的时候.上一排 c3的最佳方案是4.所以。总的最佳方案是5+4为9.这样.一排一排推下去。最右下放的数据就是最大的价值了。(注意第3排的背包容量为7的时候,最佳方案不是本身的6.而是上一排的9.说明这时候3号物品没有被选.选的是1,2号物品.所以得9.)
从以上最大价值的构造过程中可以看出。
f(n,m)=max{f(n-1,m), f(n-1,m-w[n])+P(n,m)}这就是书本上写的动态规划方程.
3.3 代码实现:
/** * */ package edu.cumt.jnotnull; /** * @author 题目:给定n个物体,其中第i个物体重量wi,价值vi ,另有一个最大载重W的背包,往里面塞东西使得总价值尽可能大 * * 令f(i,j)表示用前i个物体装出重量为j的组合时的最大价值 * f(i,j)=max{f(i-1,j), f(i-1, j-w[i])+v[i] } ,i>0, j>=w[i]; * f(i,j) = f(i-1,j) , i>0, j<w[i]; * f(0,j) = v[0] , i=0, j>=w[0]; * f(0,j) = 0, i=0, j<w[0]; * */ public class bagPro { public static void main(String[] args) { int weight[] = { 2, 2, 6, 5, 4 }; int price[] = { 6, 3, 5, 4, 6 }; int size = 10; int maxProc[][] = new int[5][size + 1]; for (int j = 0; j <= size; j++) { if (j >= weight[0]) maxProc[0][j] = price[0]; else maxProc[0][j] = 0; } for (int i = 1; i < weight.length; i++) { for (int j = 0; j <= size; j++) { if (j < weight[i]) maxProc[i][j] = maxProc[i - 1][j]; else if (maxProc[i - 1][j] >= maxProc[i - 1][j - weight[i]] + price[i]) maxProc[i][j] = maxProc[i - 1][j]; else maxProc[i][j] = maxProc[i - 1][j - weight[i]] + price[i]; } } System.out.println(maxProc[4][size]); } }
4 动态规划解决最长公共字串问题(LCS)
说明:公共子串的元素可以不相邻
如果我们记字符串Xi和Yj的LCS的长度为c[i,j],我们可以递归地求c[i,j]:
/ 0 if i<0 or j<0
c[i,j]= c[i-1,j-1]+1 if i,j>=0 and xi=xj
\ max(c[i,j-1],c[i-1,j] if i,j>=0 and xi≠xj
代码实现:
package cumt.jnotnull; /** * 最长公共子序列问题(Longest common subsequence)。 */ public class LCS { private static final int DIRECTION_XY = 0; private static final int DIRECTION_X = 1; private static final int DIRECTION_Y = 2; private char[] x; // 输入字符串1 private char[] y; // 输入字符串2 //计算时的辅助变量 private int[][] c; // x[0..i]和y[0..j]中最长公共子序列的长度为c[i+1,j+1] private int[][] b; // public LCS(String x, String y) { this.x = x.toCharArray(); this.y = y.toCharArray(); } private void reset() { c = new int[x.length + 1][y.length + 1]; b = new int[x.length + 1][y.length + 1]; } /** * <pre> * c[i,j] = 0 if i == 0 || j == 0 * c[i-1, j-1] if i,j > 0 && x[i-1] == y[j-1] * max{c[i,j-1], c[i-1,j]} if i,j > 0 && x[i-1] != y[j-1] * </pre> */ public int execute() { reset(); // 将数组初始化为0的动作在Java中不是必须的 for (int j = 0; j < y.length; j++) { c[0][j] = 0; } for (int i = 0; i < x.length; i++) { c[i][0] = 0; } for (int i = 1; i <= x.length; i++) { for (int j = 1; j <= y.length; j++) { if (x[i-1] == y[j-1]) { c[i][j] = c[i-1][j-1] + 1; b[i][j] = DIRECTION_XY; } else { if (c[i][j-1] > c[i-1][j]) { c[i][j] = c[i][j-1]; b[i][j] = DIRECTION_Y; } else { c[i][j] = c[i-1][j]; b[i][j] = DIRECTION_X; } } } } return c[x.length][y.length]; } private String printLCS() { return printLCS(x.length, y.length); } private String printLCS(int i, int j) { if (i == 0 || j == 0) return ""; switch (b[i][j]) { case DIRECTION_XY: return printLCS(i-1, j-1) + x[i-1]; case DIRECTION_X: return printLCS(i-1, j); case DIRECTION_Y: return printLCS(i, j-1); default: throw new IllegalStateException("Impossible"); } } public static void main(String[] args) { LCS lcs = new LCS("ABCBDAB", "BDCABA"); System.out.println("max length: " + lcs.execute()); System.out.println("LCS:" + lcs.printLCS()); System.out.println("\n\n"); lcs = new LCS("ACCGGTCGAGTGCGCGGAAGCCGGCCGAA", "GTCGTTCGGAATGCCGTTGCTCTGTAAA"); System.out.println("max length: " + lcs.execute()); System.out.println("LCS:" + lcs.printLCS()); } }
说明:1 该算法只能求出最长公共字符串中的一个。2 求相邻的最长公共字符串参考http://jnotnull.iteye.com/blog/418313
参考资料:
http://zh.wikipedia.org/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92