先看几类数字三角形的问题,通过对这几个问题的分析来理解有关动态规划的基本思想
数字三角形I
问题描述:
有一个由正整数组成的三角形,第一行只有一个数,除了最下行之外 每个数的左下方和右下方各有一个数,从第一行的数开始,每次可以往左下或右下走一格,直到走到三角形底端,把沿途经过的数全部加起来作为得分。如何走,使得这个得分尽量大?
分析:
如何走,是一个决策问题,很容易联想到一种贪心策略是:每次选数字大的那个方向走。然而很明显,这种决策方案是错误的。因为如果按这种方案,得到的结果是1→3→10→3,总和为17,而正确的答案是:1→2→1→20,总和为24,这种方案错在了“目光短线“。
再次分析,赋予d[i,j]的含义为以(i,j)为首的三角形的最大和,那么这个问题就转换成了求d(1,1)。很明显,d[i,j]=a[i,j]+max(d[i+1,j],d[i+1,j+1])。这里体现的一个思想就是:将大问题分解为小问题,由小问题的求解来得到大问题的答案。
这个思路是正确的,那么接下来要考虑的一个问题就是如何计算了。有三种方法,递归、递推以及记忆化搜索,接下来就分析这三种计算方法的具体做法。
方法一:递归
int solve(int i, int j) { if(i == n) return a[i][j]; else return a[i][j] + max(solve(i + 1, j), solve(i + 1, j + 1)); }
这个方法存在不必要的重复计算,低效,不推荐使用
方法二:递推
void solve() { for(int j = 1; j <= n; j++) dp[n][j] = a[n][j]; for(int i = n - 1; i >= 1; i--) for(int j = 1; j <= i; j++) dp[i][j] = a[i][j] + max(d[i + 1][j], dp[i + 1][j + 1]); }
递推自底向上,避免了重复计算,时间复杂度为O(n2)
方法三:记忆化搜索
int solve(int i, int j) { //首先初始化dp为-1 memset(dp,-1,sizeof(dp)); if(i == n) return a[i][j]; if(dp[i][j] >= 0) return dp[i][j]; dp[i][j] = a[i][j] + max(solve(i + 1, j), solve(i + 1, j + 1)); return dp[i][j]; }
这个方法就是在递归的基础上加上了记忆化,同样避免了重复计算,时间复杂度为O(n2)
对比这几种方法,可以看出重叠子问题(overlapping subproblems) 是动态规划展示威力的关键
通过这个例子,可以理解动态规划的基本思想:将大问题分解成小问题,建立子问题的描述,建立状态间的转移关系,使用递推或记忆化搜索来实现。而要使用好动态规划的几个关键的重点就是:状态的定义和状态转移方程。
数字三角形II
问题描述:
在数字三角形I的基础上稍微改变了一下。从第一行的数开始,除了某一次可以走到下一行的任意位置外,每次都只能左下或右下走一格,直到走到最下行,把沿途经过的数全部加起来。如何走,使得这个和尽量大?
分析:
这题的关键在于后效性问题,即先前的决策可能影响后续的决策,要消除后效性,就得拓展状态定义(加限制条件)来将有后效性的部分包含进去。
1 memset (maxx, 0, sizeof (maxx)); 2 for(j = 1; j <= n; j ++) 3 { 4 d[n][j][1] = d[n][j][0] = a[n][j]; 5 if(a[n][j] > max[n]) 6 maxx[n] = a[n][j]; 7 } 8 for(i = n - 1; i >= 1; i--) 9 { 10 for(j = 1; j <= i; j++) 11 { 12 d[i][j][0] = a[i][j] + max(d[i + 1][j][0], d[i + 1][j + 1][0]); 13 if(d[i][j][0] > maxx[i]) 14 maxx[i] = d[i][j][0]; 15 d[i][j][1] = a[i][j] + max(d[i + 1][j][1], d[i + 1][j + 1][1], maxx[i + 1]); 16 } 17 }
数字三角形III
问题描述:
在数字三角形I的基础上,问题变成了,如何走,使得这个和的个位数尽量大?
分析:
符合无后效性了,但是却出现了另一个问题,那就是由子问题的最优解不能推出全局的最优解。比如看下面一个例子
对于灰色格子(2,1)来说,根据状态定义,d[2,1]=6(从 此格子出发路径上数之和的最大个位数),d[2,2]=0(无论怎么走,个位数都是0), 根据前面的“递推方程”算出d[1,1]应是1,但实际上d[1,1]等于9。问题出在:全局最优解5-0-4-0并没有包含子问题最优解0-4-2,即不满足最优子结构。不满足最优子结构的情况通常也可以考虑扩展状态定义。
1 for(j = 1; j <= n; j++) 2 d[n][j][a[n][j] % 10] = true; 3 for(i = n - 1; i >= 1; i--) 4 { 5 for(j = 1; j <= i; j++) 6 { 7 for(k = 0; k <= 9; k++) 8 { 9 d[i][j][k] = false; 10 t = (10 - a[i][j]) % 10; 11 if(d[i + 1][j][t] || d[i + 1][j + 1][t]) 12 d[i][j][k] = true; 13 } 14 } 15 }
原文地址:https://www.cnblogs.com/friend-A/p/10291716.html