初学动态规划

浅谈递归,记忆化搜索和动态规划

在解决动态规划类问题时,我们常常想到的是递归。递归的时间复杂度是	O(n!),因为递归途中有许多的重复计算,以三角形问题为例:(数字为所在位置计算次数)
					1
				1	2	1
			1	  3   3	  1
		1	   4	6	  4   1

因此,递归在使用时存在的问题最大即使: 重复计算
记忆化搜索即使对递归存在的问题进行改进,将每个已经计算过的值开一个相同大小的数组进行储存,无需再重复计算,也就是说:每个位置的值 仅 计算一次
1
1 1
1 1 1
这样就会大大优化递归的时间复杂度:O(n^2),当然空间上也有,对于递归来说容易爆栈。这样对于递归问题,我们都可以采用记忆化搜索优化并替代。

动态规划解决问题的思想与递归别无二致,都是:
寻找子问题,
确定状态,
寻找边界,
得到转移方程,

但是动态规划推荐使用递推完成,实际上,虽然思想相同,但是实际上递推又是在一定程度上 减少了程序时间。
另一方面,在空间复杂度上,递推可以采取“滚动数组”的方式节约空间花费,而利用递归却不行,还是三角形的例子,可以采取 所得最大值 储存不采用另外开辟的数组,而是 以 原数组的 最后一行 作为 所求值得存储位置。
请看下边的例子:

数字三角形案例:

题目描述:
	给出一个数字三角形(示例如下图),从最上边的三角形开始,每次只能向左下或右下走,直到最后一行,请选择经过数字总和最大的路线;
   				5
   			1		3
   		2		4		1
   	8		7		9		10
   	将其看作:
   	5
   	1 3
   	2 4 1
   	8 7 9 10
   	即只能向下或右下移动
   	
 (	从数组下标1开始保存 )
子问题:第n行到a[1][1]的max-->第n行到第2行的max+a[1][1]
	   第n行到a[2][j]的max-->第n行到第3行的max+a[2][j]
	   ****
	   第n行到a[n-1][j]的max-->第n行到第n行的max+a[n-1][j];
状态:
		用maxSum(k,j)表示第n行到a[k][j]位置的路线最大值
边界问题:
		最小子问题为 maxSum(n,j)=a[n][j];
		即,第n行j列到第n行的最大值是元素本身a[n][j];
转移方程:
		if(k==n)	返回a[n][j];
		maxSum(k,j)= a[k][j]+max(maxSum(k+1,j),maxSum(k+1,j+1));

代码如下:

递归

int n;	//行数	
int maxSum(int k, int j)
{
	if (k == n)
		return a[k][j];
	int x = maxSum(k + 1, j);
	int y = maxSum(k + 1, j + 1);
	return a[k][j] + max(x, y);
}

记忆化搜索

const int MAX = 10e6;
vector<int> dp[MAX];	//给个二维数组即可,但是大小要保证
int maxSum(int k, int j)
{
	//用引用可以简略大量代码
	int& d = dp[k][j];
	//初始化dp为0,所以只要被赋值,就>=0
	if (d > 0)	return d;
	//
	if (k == n)	return a[k][j];
	int x = maxSum(k + 1, j);
	int y = maxSum(k + 1, j + 1);
	//也可以分开写,先赋值后return
	return d = max(x, y) + a[k][j];
}

递推

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int k, j, n;
	for(int k = n;k >= 1;--k)
		for (int j = 1; j <= k; ++j)
		{	//STL的max函数
			dp[k][j] = max(dp[k + 1][j], dp[k + 1][j + 1]) + a[k][j];
		}
	return 0;
}

递推(优化空间)

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int k, j, n;
	int* p = &a[n][0];	//滚动层
	//注意界限的变化,递推实现只能逆推,从n-1层开始
	for (int k = n - 1; k >= 1; --k)
		for (int j = 1; j <= k; ++j)
		{	//STL的max函数
			p[j] = max(p[j], p[j + 1]) + a[k][j];
		}
	return 0;
}

从 递归–》记忆化搜索,只需要加一个dp数组避免重复
从 递归–》动态规划,思路一致;
从 动规–》优化,替换指针即可
每一次的改进只是小变动,是不是很美?!待续…>

发布了18 篇原创文章 · 获赞 0 · 访问量 349
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 游动-白 设计师: 上身试试

分享到微信朋友圈

×

扫一扫,手机浏览