码迷,mamicode.com
首页 > 编程语言 > 详细

笨办法理解动态规划算法

时间:2019-06-08 19:09:40      阅读:190      评论:0      收藏:0      [点我收藏+]

标签:one   转换   需要   mic   ==   最优   形式   通过   图片   

动态规划在编程中有着广泛的应用,对于某些问题我们可以通过动态规划显著的降低程序的时间复杂度。本质上动态规划并不是一种算法,而是解决一类问题的思想。本篇博客通过一些非常简单而又经典的问题(比如数塔、0-1背包、完全背包、走楼梯问题、最长公共子序列等)来帮助大家理解动态规划的一般套路。

欢迎探讨,如有错误敬请指正

如需转载,请注明出处 http://www.cnblogs.com/nullzx/

1 动态规划的基本思想

如果我们解决一个问题的时候能将一个大问题转换成一个或者若干个规模较小的同等性质的问题,当我们求解出这些小问题的答案后,大问题的答案很容易解决,对于这样的情况,显然我们可以递归(或者说分治)的方式解决问题。如果在求解这些小问题的过程中发现有些小问题我们需要重复计算多次,那么我们就干脆把已经求解过的小问题的答案记录下来放在一张表中,这样下次遇到这个小问题,我们只需要查表就可以直接得到结果,这个就是动态规划的白话讲解。动态规划的难点在于如何定义问题及子问题。

2. 笨办法的套路

1)如果可以将一个规模较大的问题转换成一个或若干个规模较小的子问题,也就是能找到递推关系,这个时候我们不妨先将程序写成递归的形式。

2)如果使用递归求解规模较小的问题上存在子问题重复求解的现象,那么我们就建立一张表(有可能这个表只有一行)记录需要重复求解的子问题。填表的过程和将大问题划分为子问题的方式相反,我们会从最简单的子问题开始填表。现在我们就利用这个套路解决下面这些经典的问题。

3.利用套路解题

3.1 菲波那切数列

问题描述:菲波那契数列的定义f(n) = f(n-1) + f(n-2), 且f(1)=1, f(2) = 1,求f(n)的值。斐波那契数列的定义本身就是将大问题转换成两个同性质的子问题,所以我们可以直接根据定义写成递归形式。


	public static int recursion(int n) {
		
		if (n < 0) {
			return 0;
		}
		
		if (n == 1 || n == 2) {
			return 1;
		}
		
		return recursion(n-1) + recursion(n-2);
	}

我们以f(6)为例现在把递归的过程画出来

技术图片

我们发现在求解F(6)时,需要求解F(2)四次,求解F(1)三次,求解F(3)三次,F(4)两次,所以说我们的算法的效率是很低的。提高效率的办法就是将F(1),F(2),F(3) ….的结果放在表中,下次要计算这些问题的时候我们直接从表中获取就好了,这就是一个最简单的动态规划的例子。现在我们按照套路,从最小的子问开始填表就好了。


	public static int dynamic(int n) {
		
		int[] table = new int[n+1];
		
		table[1] = 1;
		table[2] = 1;
		
		/*从小到大填表*/
		for (int i = 3; i < table.length; i++) {
			table[i] = table[i-1] + table[i-2];
		}
		
		return table[n];
	}

需要说明的是,这个例子只是一个入门的例子,实际上它不存在最优子结构的问题,而且也不需要长度为n+1的table数组,只需要两个变量即可(可以理解为动态规划的优化版本),而我们之所以这样讲解只是为了让大家从动态规划的角度去理解问题。

笨办法理解动态规划算法

标签:one   转换   需要   mic   ==   最优   形式   通过   图片   

原文地址:https://www.cnblogs.com/nullzx/p/10991305.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!