动态规划DP

1、切割钢条

一条N米长的钢条,不同长度价格不同,如下:

长度12345678910
价格1589101717202430

可以用递归的方法解决,但是会消耗过多的时间的,因为当计算相同的一段长度时,时间还是双倍的,而不能借助之前已经计算出来的结果。


所以这是典型的时空权衡的例子。我们采用动态规划。


这里动态规划有两种,一种是从顶而下,第二种自底而上,这里介绍第二种。


public class Cutting_Bars {

	int[] BOTTOM_UP_CUT_BAR(int[] p , int n){//传入钢条长度价格,实际拿到的钢条长度n
		
		int r[] = new int[n+1];//初始化记忆dp,记录已经计算过的长度最优价格
		
		r[0] = 0;//初始化
		
		for(int i = 1 ; i <= n ; i ++){
			
			int q = -1;

			for(int j = 1 ; j <= i ; j ++){//查找i长度内最优切割价格
				
				if (j >= p.length) {//判断j长度是否超出了价格表p的最大长度
					
					q = max(q, p[p.length - 1] + r[i-(p.length - 1)]);
					
					break;
					
				}else {
					
					q = max(q,p[j] + r[i-j]);//将i长度分两部分,j长度,i-j长度,j长度直接参考价格表,i-j长度参考最优r数组内的值
					
				}
				
				
				
			}
			
			r[i] = q;
			
		}
		
		return r;

	}
	
	public static void main(String[] args) {
		
		int[] p = {0,1,5,8,9,10,17,17,20,24,30};
		
		Cutting_Bars cuttingBars = new Cutting_Bars();
		
		int[] r = cuttingBars.BOTTOM_UP_CUT_BAR(p, 20);
		
		System.out.println(r[20]);
		
	}
	
	
	
	private int max(int x , int y ){
		
		if (x > y) {
			
			return x;
			
		}else {
			return y;
		}
		
	}
	
}



2、最长递增子串

Longest_Increasing_Subsequence


输入 128 342 234 355 653 123 345

找出该串最长的递增子串的长度

输出:4(128 234 355 653或者 128 342 355 653)


public class Long_Increasing_Subsequence {
	
	public static void main(String[] args) {
		
		Scanner scan = new Scanner(System.in);
		
		int num = scan.nextInt();
		
		int[] ints = new int[num];
		
		scan.nextLine();
		
		for(int i = 0 ; i < num ; i ++){
			
			ints[i] = scan.nextInt();
			
		}
		
		int[] dp = new int[ints.length];//保存每个值对应最长递增子串长度
		
		for(int i = 0 ; i < ints.length ; i ++){
			
			dp[i] = 1;//默认自己既是符合条件的递增子串,长度为1
			
			for(int j = 0 ; j < i ; j ++){//查找第i个值(包括i)之前值最长递增子串长度为多少,第i个值作为该最长子串的子串尾
				
				if(ints[i] > ints[j] && dp[i] < dp[j] + 1){
					
					dp[i] = dp[j] + 1;
					
				}
				
			}
			
		}
		
		int tmp = dp[0];
		
		for(int i = 1 ; i < dp.length ; i ++){//输出dp内最长递增子串
			
			if(tmp < dp[i])
				tmp = dp[i];
			
		}
		
		System.out.println(tmp);
		
	}
	
}


3、最长公共子串

Longest_Common_Subsequece


输入:A,B,C,B,D,A,B

    B,D,C,A,B,A


查找该两串中,间隔的最长公共子串,即:按顺序从左到右双方都含有的最长子串


输出:B,C,B,A


三种情况:

X=<x1,x2,x3,x4,...,xm>,Y=<y1,y2,y3,y4,...,yn>,Z=<z1,z2,z3,z4,...,zk>为X和Y的任意LCS

1)xm = yn,则zk = xm=yn且Zk-1是Xm-1和Yn-1的一个LCS

2)xm<>yn,则zk<>xm意味着Z是Xm-1和Y的一个LCS

3)xm<>yn,则zk<>yn意味着Z是X和Yn-1的一个LCS


得到状态方程

c[][]表示Xi和Yj的LCS的长度

c[i][j] = 0 若i=0,或j=0

c[i][j] = c[i-1][j-1] + 1 若i,j>0且xi = yj

c[i][j] = max(c[i][j-1] , c[i-1][j]) 若i,j>0且xi <> yj


还构造了b[][]帮助构造最优解 0来自左上方,1来自上方,2来自左边




public class Longest_Common_Subsequence {
	
	private int c[][];//保存
	public int b[][];

	int getLCS_length(char[] x ,char[] y){
		
		int x_len = x.length;
		int y_len = y.length;
		
		c = new int[x_len+1][y_len+1];
		b = new int[x_len+1][y_len+1];//0 左上方,1上方,2左方
		
		for(int i = 0 ; i <= x_len ; i ++)
			c[i][0] = 0;
		for(int i = 0 ; i <= y_len ; i++)
			c[0][i] = 0;
		
		for(int i = 1 ; i <= x_len ; i ++){
			
			for(int j = 1 ; j <= y_len ; j ++){
				
				if (x[i-1] == y[j-1]) {
					
					c[i][j] = c[i-1][j-1] + 1;
					b[i][j] = 0;//左上方
					
				}else if(c[i][j-1] >= c[i-1][j]){
					
					c[i][j] = c[i][j-1];
					b[i][j] = 2;//上方
					
				}else {
					
					c[i][j] = c[i-1][j];
					b[i][j] = 1;//左方
					
				}
				
			}
			
		}
		
		return c[x_len][y_len];
		
	}
	
	void print_lcs(int[][] b,char[] x,int x_len,int y_len){
		
		if (x_len == 0 || y_len == 0) {
			return;
		}
		
		if (b[x_len][y_len] == 0) {
			
			print_lcs(b, x, x_len-1, y_len-1);
			
			System.out.println(x[x_len-1]);
			
		}else if (b[x_len][y_len] == 1) {
			
			print_lcs(b, x, x_len-1, y_len);
		}else {
			print_lcs(b, x, x_len, y_len-1);
			
		}
		
	}
	
	public static void main(String[] args) {
		
		char[] x = {'a','b','c','b','d','a','b','b'};
		char[] y = {'b','d','c','a','b','a','c'};
		
		Longest_Common_Subsequence lcs = new Longest_Common_Subsequence();
		
		System.out.println(lcs.getLCS_length(x, y));
		
		lcs.print_lcs(lcs.b, x, x.length, y.length);
		
	}
	
}


4、最长回文子串

Longest_Palindrome_Subsequece


何为回文?

输入:character

输出:carac

carac即为回文


这里分两种:1、连续回文最长子串;2、不连续回文最长子串


先说第二种:

不连续回文最长子串,其实就是把该子串倒一下,进行最长公共子串操作就能得出结果


第一种:


这里规定,单个字符肯定是回文子串


一个n长度的字符串,其中第i个字符到第j个字符之间为回文子串


那么,第i+1个字符到第j-1个字符肯定也是回文子串,以此类推,直到i+x=j-x的时候(即该回文子串为奇数个长度时)或者i+x+1=j-x并且第i+x个字符与第j-x个字符相同(即该回文子串为偶数个长度时)


public class Longest_Palindrome_Subsequence {

	private int c[][];
	public int b[][];
	
	/**
	 * 连续的最长回文子串
	 * @param x
	 * @return
	 */
	char[] getLps(char[] x){
		
		boolean[][] table = new boolean[1000][1000];
		
		int n = x.length;
		
		int longestBegin = 0;
		int maxLen = 1;
		
		for(int i = 0 ; i < n ; i ++)//初始化,单个字符都为回文子串
			table[i][i] = true;
			
		for(int i = 0 ; i < n - 1 ; i ++){//初始化,判断相邻的字符是否相等,即判断回文子串长度为2的子串有哪些
			
			if (x[i] == x[i+1]) {
				
				table[i][i+1] = true;
				
				longestBegin = i;
		
				maxLen = 2;
				
			}
			
		}
		
		for(int len = 3 ; len <= n ; len ++){//理解其思想
			
			for(int i = 0 ; i <= n-len ; i ++){//从长度为3的子串开始一个个判断,长度为2的子串之前已经都判断过了,现在在其基础上进行判断回文子串
				
				int j = i + len - 1;
				
				if(x[i] == x[j] && table[i+1][j-1]){
					
					table[i][j] = true;
					
					longestBegin = i;
					
					maxLen = len;
					
				}
				
			}
			
		}
	
		char[] tmp = new char[maxLen];
		
		for(int i = longestBegin ; i < longestBegin+maxLen ; i ++){
			
			tmp[i-longestBegin] = x[i];
			
		}
		
		return tmp;
		
	}
	
	/**
	 * 获得不连续回文子串
	 * @param x
	 * @return
	 */
	void get_Incontinulity_Lps(char[] x){
		
		int len = x.length;
		
		char[] y = new char[len];
		
		for(int i = x.length-1 ; i >= 0 ; i --)
			y[x.length -1 -i] = x[i];
		
		c = new int[len+1][len+1];
		b = new int[len+1][len+1];
		
		System.out.println(x);
		System.out.println(y);
		
		for(int i = 0 ; i <= len ; i ++){
			
			c[i][0] = 0;
			c[0][i] = 0;
			
		}
		
		for(int i = 1 ; i <= len ; i ++){
			
			for(int j = 1 ; j <= len ; j ++){
				
				if (x[i-1] == y[j -1]) {
				
					c[i][j] = c[i-1][j-1] + 1;
					
					b[i][j] = 0;//左上方
					
				}else if (c[i][j-1] >= c[i-1][j]) {
					
					c[i][j] = c[i][j-1];
					
					b[i][j] = 2;//左边
					
				}else {
					
					c[i][j] = c[i-1][j];
					
					b[i][j] = 1;//上面
					
				}
				
			}
			
		}
		
		System.out.println(c[len][len]);
		
		
		
	}
	
	void print_lps(int[][] b,char[] x,int i , int j){
		
		if (i == 0 || j == 0)
			return;
		
		if(b[i][j] == 0){
			
			print_lps(b, x, i-1, j-1);
			
			System.out.println(x[i -1]);
			
		}else if (b[i][j] == 1) {
			
			print_lps(b, x, i-1, j);
			
		}else {
			
			print_lps(b, x, i, j-1);
			
		}
		
	}
	
	public static void main(String[] args) {
		
		char[] str = {'c','h','a','r','a','c','t','e','r'};
		
		Longest_Palindrome_Subsequence lps = new Longest_Palindrome_Subsequence();
		
//		char[] tmp = lps.getLps(str);
		
//		System.out.println(tmp);
		
		lps.get_Incontinulity_Lps(str);
		
		lps.print_lps(lps.b, str, str.length, str.length);
		
	}
	
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值