最长公共子序列--动态规划引入

    一学期前曾经学习过一点动态规划,但是云里雾里,感觉玄乎其玄。不知所以然就把无解的问题解决了,很是牛逼。

 今天有幸看到了一篇博文。教你彻底学会动态规划——入门篇http://blog.csdn.net/baidu_28312631/article/details/47418773。实在是受益匪浅。由此打开新世界的大门,记录下我自己的理解。

    首先引入一个问题如下,数字三角形(POJ1163)

    

   在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大。路径上的每一步都只能往左下或 右下走。只需要求出这个最大和即可,不必给出具体路径。 三角形的行数大于1小于等于100,数字为 0 - 99

    这道题讲的有点复杂,我们可以这么看,

    5      

    7

    3   8

    8   1   0

    2   7   4   4

    4   5   2   6   5

从顶点开始往下走,只可以向下或者向右下方走。把经过的路径上的数字累加起来,求出最大值。

为了简便说明,我们使用 matrix[i,j] 代表第 i 行 第 j 个元素,使用 maxSum[i,j]表示从位置 matrix[i,j] 到终点的路径值,那么题目就是求 maxSum[0,0]。

经过某一个位置时 matrix[i,j] 时,可以经过 matrix[i+1,j] 或者 matrix[i+1,j+1].

我们试着想一下递推公式

注意经过最后一行时 maxSum[i,j] 就是最后一行元素的值。

if(i==n-1)
   maxSum[i,j]=matrix[i,j];
else
   maxSum[i,j]=matrix[i,j]+max{maxSum[i+1,j],maxSum[i+1,j+1]};

所以我们可以敲下代码


	/**
	 * 获取路径最大值
	 */
	private void calculate(){
		int max=getSumMax(0,0);
		System.out.println("从顶到底路径最大值为:"+max);
	}
	
	
	private int getSumMax(int i,int j){
		if(i==lineCount-1)
			return inputData[i][j];
		int x=getSumMax(i+1,j);
		int y=getSumMax(i+1,j+1);
		return Math.max(x, y)+inputData[i][j];
	}

给出完整代码

package com.luo.dongtaiguihua;

import java.util.Scanner;

import com.luo.util.SortUtils;
/*
 * 初识动态规划
 * 效率底下,即递归又不备忘
 * */
public class DongTaiGuiHuaLowest {

	int lineCount;
	int[][] inputData;
	
	public void run(){
		
		 input();
		 SortUtils.printArray(inputData);
		 calculate();
	}
	/*
	 * 输入数据
	 * */
	private void input(){
		Scanner in=new Scanner(System.in);
		lineCount=Integer.parseInt(in.nextLine());
		inputData=new int[lineCount][lineCount];
		for(int i=0;i<lineCount;i++){
			String line=in.nextLine();
			String[] strs=line.split(" ");
			int len=strs.length;
			for(int j=0;j<len;j++){
				inputData[i][j]=Integer.parseInt(strs[j]);
			}
		}
	}
	/**
	 * 获取路径最大值
	 */
	private void calculate(){
		int max=getSumMax(0,0);
		System.out.println("从顶到底路径最大值为:"+max);
	}
	
	
	private int getSumMax(int i,int j){
		if(i==lineCount-1)
			return inputData[i][j];
		int x=getSumMax(i+1,j);
		int y=getSumMax(i+1,j+1);
		return Math.max(x, y)+inputData[i][j];
	}
}

但是,这样子计算速度不敢恭维。想象一下,从matrix[0,0] 开始,第二次递归计算 maxSum[1,0] 和 maxSum[1,1] ,maxSum[1,0] 又计算 maxSum[2,0] 和 maxSum[2,1] 而且最为致命的是,如果我费了大半天已经计算出来 maxSum[2,1],但是下次计算maxSum[2,1] ,我仍然会递归循环计算该值,导致很多不必要的计算。

红色角标代表该位置到终点计算的次数。可以看出越是底层越是重复计算

24134633_4e89.jpg

 

那我们是不是可以将计算的储存结果储存起来,下次调用先查看该结果,如果有缓存了那么就直接使用就可以了,不用再次计算了。

很明显可以使用一个二维数组储存计算结果

代码如下


	//递归算法
	private int getSumMax(int i,int j){
		if(outputData[i][j]!=-1)
			return outputData[i][j];
		int x=getSumMax(i+1,j);
		int y=getSumMax(i+1,j+1);
		int res=Math.max(x, y)+inputData[i][j];
		outputData[i][j]=res;
		return res;
	}

 

再做一些优化,这种方法是使用递归算法的,无形中就使用了大量的堆栈资源,如果调用深度太深,那么就会造成栈溢出,所以我们可以使用递推算法实现,减少资源使用。

递推很明显只可以从底部开始往上递推,创建一个二维数组,从最后一行开始更新,过程如下

24134633_GYG8.jpg

24134633_l0zz.jpg

24134633_WA95.jpg

24134633_kAri.jpg

 

知道了思路,那么就开始实现吧


	//非递归算法,递推算法实现
	private int getSumMax(int i,int j){
		
		for(int p=lineCount-1-1;p>=0;p--){
			for(int q=0;q<=p;q++){
				outputData[p][q]=Math.max(outputData[p+1][q], outputData[p+1][q+1])+inputData[p][q];
			}
		}
		return outputData[0][0];
	}

 

下面给出完整代码

package com.luo.dongtaiguihua;

import java.util.Scanner;

import com.luo.util.SortUtils;

/*
 * 低阶动态规划
 * 使用二维数组缓存,使用递归算法和递推算法实现
 * */
public class DongTaiGuiHuaLower {

	int lineCount;
	int[][] inputData;
	int[][] outputData;
	
	public void run(){
		
		 input();
		 SortUtils.printArray(inputData);
		 calculate();
		 SortUtils.printArray(outputData);
	}
	/*
	 * 输入数据
	 * */
	private void input(){
		Scanner in=new Scanner(System.in);
		lineCount=Integer.parseInt(in.nextLine());
		inputData=new int[lineCount][lineCount];
		for(int i=0;i<lineCount;i++){
			String line=in.nextLine();
			String[] strs=line.split(" ");
			int len=strs.length;
			for(int j=0;j<len;j++){
				inputData[i][j]=Integer.parseInt(strs[j]);
			}
		}
	}
	/**
	 * 获取路径最大值
	 */
	private void calculate(){
		initOutputData();
		int max=getSumMax(0,0);
		System.out.println("从顶到底路径最大值为:"+max);
	}
	/*
	 * 初始化用于缓存的数组
	 * */
	private void initOutputData() {
		outputData=new int[lineCount][lineCount];
		for(int i=0;i<lineCount;i++){
			outputData[lineCount-1][i]=inputData[lineCount-1][i];
		}
		for(int i=0;i<lineCount-1;i++){
			for(int j=0;j<lineCount;j++){
				outputData[i][j]=-1;
			}
		}
	}
	
//	//递归算法
//	private int getSumMax(int i,int j){
//		if(outputData[i][j]!=-1)
//			return outputData[i][j];
//		int x=getSumMax(i+1,j);
//		int y=getSumMax(i+1,j+1);
//		int res=Math.max(x, y)+inputData[i][j];
//		outputData[i][j]=res;
//		return res;
//	}
	

	//非递归算法,递推算法实现
	private int getSumMax(int i,int j){
		
		for(int p=lineCount-1-1;p>=0;p--){
			for(int q=0;q<=p;q++){
				outputData[p][q]=Math.max(outputData[p+1][q], outputData[p+1][q+1])+inputData[p][q];
			}
		}
		return outputData[0][0];
	}
}

 

其实这还可以再做一些优化,其实可以使用一维数组就够了

 

完整代码如下

package com.luo.dongtaiguihua;

import java.util.Scanner;

import com.luo.util.SortUtils;

/*
 * 低阶动态规划
 * 使用递推算法和一维数组实现
 * */
public class DongTaiGuiHuaLow {

	int lineCount;
	int[][] inputData;
	int[] outputData;
	
	public void run(){
		
		 input();
		 SortUtils.printArray(inputData);
		 calculate();
		 SortUtils.printArray(outputData);
	}
	/*
	 * 输入数据
	 * */
	private void input(){
		Scanner in=new Scanner(System.in);
		lineCount=Integer.parseInt(in.nextLine());
		inputData=new int[lineCount][lineCount];
		for(int i=0;i<lineCount;i++){
			String line=in.nextLine();
			String[] strs=line.split(" ");
			int len=strs.length;
			for(int j=0;j<len;j++){
				inputData[i][j]=Integer.parseInt(strs[j]);
			}
		}
	}
	/**
	 * 获取路径最大值
	 */
	private void calculate(){
		initOutputData();
		int max=getSumMax(0,0);
		System.out.println("从顶到底路径最大值为:"+max);
	}
	/*
	 * 初始化用于缓存的数组
	 * */
	private void initOutputData() {
		outputData=new int[lineCount];
		for(int i=0;i<lineCount;i++){
			outputData[i]=inputData[lineCount-1][i];
		}
	}
	

	//非递归算法,使用递推算法实现
	private int getSumMax(int i,int j){
		for(int p=lineCount-1-1;p>=0;p--){
			for(int q=0;q<=p;q++){
				outputData[q]=inputData[p][q]+Math.max(outputData[q], outputData[q+1]);
			}
		}
		return outputData[0];
	}
}

 

转载于:https://my.oschina.net/u/2254579/blog/866097

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值