动态规划(2):动态规划的三种形式
来源:互联网 发布:Mac玩钢铁雄心4 编辑:程序博客网 时间:2024/06/05 08:14
例:数字三角形(POJ 1163)
Description
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
(Figure 1)
Input
Output
Sample Input
573 88 1 0 2 7 4 44 5 2 6 5
Sample Output
30
Source
基本思路
用二维数组存放数字三角形。
D( r, j) : 第r行第 j 个数字(r,j从1开始算)
MaxSum(r, j) : 从D(r,j)到底边的各条路径中,
最佳路径的数字之和。
问题:求 MaxSum(1,1)
典型的递归问题。
D(r, j)出发,下一步只能走D(r+1,j)或者D(r+1, j+1)。故对于N行的三角形:
if ( r == N) MaxSum(r,j) = D(r,j)else MaxSum( r, j) = Max{ MaxSum(r+1,j), MaxSum(r+1,j+1) } + D(r,j)
然后你会发现:
为什么超时
重复计算
如果采用递规的方法,深度遍历每条路径,存在大量重复计算。则时间复杂度为 2n,对于 n = 100 行,肯定超时。
改进
如果每算出一个MaxSum(r,j)就保存起来,下次用到其值的时候直接取用,则可免去重复计算。那么可以用O(n2)时间完成计算。因为三角形的数字总数是 n(n+1)/2
改进代码
#include <iostream>#include <algorithm>#define MAX 101using namespace std;int D[MAX][MAX]; int n;int maxSum[MAX][MAX];int MaxSum(int i, int j) { if (maxSum[i][j] != -1) return maxSum[i][j]; if (i == n) maxSum[i][j] = D[i][j]; else { int x = MaxSum(i + 1, j); int y = MaxSum(i + 1, j + 1); maxSum[i][j] = max(x, y) + D[i][j]; } return maxSum[i][j];}int main(){ int i, j; cin >> n; for (i = 1; i <= n; i++) for (j = 1; j <= i; j++) { cin >> D[i][j]; maxSum[i][j] = -1; } cout << MaxSum(1, 1) << endl;}
转化为递推
“人人为我”型递推
#include <iostream>#include <algorithm>#define MAX 101using namespace std;int D[MAX][MAX]; int n;int maxSum[MAX][MAX];int main(){ int i, j; cin >> n; for (i = 1; i <= n; i++) for (j = 1; j <= i; j++) cin >> D[i][j]; for (int i = 1; i <= n; ++i) maxSum[n][i] = D[n][i]; for (int i = n - 1; i >= 1; --i) for (int j = 1; j <= i; ++j) maxSum[i][j] = max(maxSum[i + 1][j], maxSum[i + 1][j + 1]) + D[i][j]; cout << maxSum[1][1] << endl;}
这里说的人人为我型递归就是在求DP[i][j]的过程中, 使用DP[i-1][1]→DP[i - 1][i - 1]来推到出DP[i][j];
“我为人人”型递归
#include <iostream>#include <algorithm>#define MAX 101using namespace std;int D[MAX][MAX]; int n;int maxSum[MAX][MAX];int main(){ int i, j; cin >> n; for (i = 1; i <= n; i++) for (j = 1; j <= i; j++) cin >> maxSum[i][j]; for (int i = 1; i < n; i++) { for (int j = 1; j <= i; j++) { maxSum[i + 1][j] = max(maxSum[i + 1][j], maxSum[i + 1][j] + maxSum[i][j]); maxSum[i + 1][j + 1] = max(maxSum[i + 1][j + 1], maxSum[i + 1][j + 1] + maxSum[i][j]); } } int maxx = -9999999999; for (int i = 1; i <= n; i++) maxx = max(maxx, maxSum[n][i]); cout << maxx << endl;}
这里说的人人为我型递归就是在求DP[i][j]的过程中, 求出DP[i + 1][j]和DP[i + 1][j +1]的其中一个解。
空间优化
没必要用二维maxSum数组存储每一个MaxSum(r,j),只要从底层一行行向上递推,那么只要一维数组maxSum[100]即可,即只要存储一行的MaxSum值就可以。
进一步考虑,连maxSum数组都可以不要,直接用D的第n行替代maxSum即可。
节省空间,时间复杂度不变
#include <iostream>#include <algorithm>#define MAX 101using namespace std;int D[MAX][MAX];int n; int * maxSum;int main() { int i, j; cin >> n; for (i = 1; i <= n; i++) for (j = 1; j <= i; j++) cin >> D[i][j]; maxSum = D[n]; //maxSum指向第n行 for (int i = n - 1; i >= 1; --i) for (int j = 1; j <= i; ++j) maxSum[j] = max(maxSum[j], maxSum[j + 1]) + D[i][j]; cout << maxSum[1] << endl;}
·
·
·
总结三种动态规划的形式
人人为我
状态i的值Fi由若干个值已知的状态值Fk,Fm,..Fy推出,如求和,取最大值
我为人人
状态i的值Fi在被更新(不一定是最终求出)的时候,依据Fi去更新(不一定是最终求出)和状态i相关的其他一些状态的值Fk,Fm,..Fy
注意:在选取最优备选状态的值Fm,Fn,…Fy时,有可能有好的算法或数据结构可以用来显著降低时间复杂度。
记忆型递归
优点:只经过有用的状态,没有浪费。递推型会查看一些没用的状态,有浪费
缺点:可能会因递归层数太深导致爆栈,函数调用带来额外时间开销。无法使用滚动数组节省空间。总体来说,比递推型慢。
- 动态规划(2):动态规划的三种形式
- 背包问题的动态规划形式解
- 动态规划示例三
- 动态规划三
- 动态规划入门<三>
- 动态规划(三.LCS)
- 动态规划之三
- 动态规划!!!动态规划!!!
- 动态规划的状态表示(三)
- 三、动态规划问题的特征
- 动态规划的三大要素
- 动态规划的思考(三)
- 动态规划----贪心的动态规划问题
- 动态规划入门(讲故事的形式,很棒!)
- 【动态规划】三种基本背包问题
- poj 1159 Palindrome 动态规划的三种解法
- 【三】离散化动态规划
- 动态规划问题详解(三)
- 实例学习ansible系列(12)常用模块之docker
- Django基础内容总结
- STM32之DAC
- 选择系统相册或拍照显示图片到ImageView
- Java——static修饰符、final修饰符、abstract修饰符、接口
- 动态规划(2):动态规划的三种形式
- Html5中Canvas绘图
- Ajax在跨域情况下async:false失效
- poj2965_refrigerator(BFS+枚举)
- 测试caffe中的net
- 【 同 余 定 理 (补充)】
- 面试经历(不断更新)
- POJ2082(简单堆栈)
- 面向对象练习