动态规划

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

 

动态规划_第1张图片

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

http://blog.chinaunix.net/u2/62281/showart_488401.html

http://www.gzu521.com/campus/article/it/200612/131825.htm

你可能感兴趣的:(C++,c,C#,F#,J#)