【动态规划】动态规划解题的一般思路——以数字三角形为例
2017-09-20 22:37
239 查看
/*(POJ1163)数字三角形
*在数字三角形 中寻找一条从顶部到底边的路径,
* 使得路径上所经过的数字之和最大,路径上的每一步都只能往左下或者右下走。
*只需要求处这个最大和即可,不必给出具体路径
输入格式:
5 // 三角形行数。下面是三角形
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
要求输出最大和
*/
解题思路:
- 我们用一个二维数组D[r][j]储存第r行第j个数字,
- 函数MaxSum(i,j) 求从D(r,j)到底边的各条路径中,最佳路径的数字之和。
则可将此问题转化为典型的递归问题:
从D( i , j )出发,下一步只能走D( i+1, j ) 或者 D( i+1, j+1 )。
此时我们要求出从底边到 第i行 j列 的最佳路径MaxSum( i ,j ),
1. 先求出MaxSum(i+1, j)和MaxSum( i+1, j+1 ),
2. MaxSum( i , j )的值为其下第 i+1行的两个可进入的MaxSum的值中的较大者 + D[i][j]
3. 考虑边界,其最底行的MaxSum即为其本身
代码实现如下:
以这种方式,我们实现了动态规划最关键的第一步:
1.将原问题分解为子问题
子问题与原问题形式相同或相似,不过规模变小,只要子问题解决,原问题便解决。
例:在数字三角形中,要求MaxSum( i , j ) 则必须先求规模更小的 MaxSum( i+1, j ) 和MaxSum( i+1, j+1)并比较其大小。
子问题的解一旦求出就会被保存,所以每个子问题只需求解一次。
2.确定状态
在用动态规划解题时,我们往往和子问题相关的各个变量的一组取值,称为一个“状态”。一个“状态”对应与一个或多个子问题。某个“状态”下的“值”,就是这个状态所对应的子问题的解。
例:在数字三角形中 MaxSum(i,j)的一组取值(i , j)即为一个状态
同时,每个状态的值仅需计算一次,我们要用一个数组将所有计算的值储存起来
3. 确定初始状态(边界条件)
以数字三角形为例,初始状态就是底边数字,值就是底边数字。
4.确定状态转移转移方程
即如何一个或多个“值”已知的状态,求出另一个“状态”的值。
数字三角形的状态转移方程:
MaxSum[r][j] = { D [i] [j] ( 当 r = N ,边界情况) } 或 { Max ( MaxSum[ r + 1 ] [ j ] , MaxSum[ r + 1 ] [ j + 1] ) + D [ r ] [ j ] (其他情况) }
由递归到递推
*在数字三角形 中寻找一条从顶部到底边的路径,
* 使得路径上所经过的数字之和最大,路径上的每一步都只能往左下或者右下走。
*只需要求处这个最大和即可,不必给出具体路径
输入格式:
5 // 三角形行数。下面是三角形
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
要求输出最大和
*/
解题思路:
- 我们用一个二维数组D[r][j]储存第r行第j个数字,
- 函数MaxSum(i,j) 求从D(r,j)到底边的各条路径中,最佳路径的数字之和。
则可将此问题转化为典型的递归问题:
从D( i , j )出发,下一步只能走D( i+1, j ) 或者 D( i+1, j+1 )。
此时我们要求出从底边到 第i行 j列 的最佳路径MaxSum( i ,j ),
1. 先求出MaxSum(i+1, j)和MaxSum( i+1, j+1 ),
2. MaxSum( i , j )的值为其下第 i+1行的两个可进入的MaxSum的值中的较大者 + D[i][j]
3. 考虑边界,其最底行的MaxSum即为其本身
代码实现如下:
#include <iostream> #include <algorithm> #define MAX 101 using namespace std; int N; //储存三角形的行数 int D[MAX][MAX]; int maxSum[MAX][MAX]; int MaxSum(int i,int j) //第r行第j个数字(r,j从1开始) { 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() { cin >> N; for(int i = 1;i <= N; ++i) for(int j = 1;j <= i;++j) { cin >> D[i][j]; maxSum[i][j] = -1; } cout << MaxSum(1,1) << endl; return 0; }
以这种方式,我们实现了动态规划最关键的第一步:
1.将原问题分解为子问题
子问题与原问题形式相同或相似,不过规模变小,只要子问题解决,原问题便解决。
例:在数字三角形中,要求MaxSum( i , j ) 则必须先求规模更小的 MaxSum( i+1, j ) 和MaxSum( i+1, j+1)并比较其大小。
子问题的解一旦求出就会被保存,所以每个子问题只需求解一次。
2.确定状态
在用动态规划解题时,我们往往和子问题相关的各个变量的一组取值,称为一个“状态”。一个“状态”对应与一个或多个子问题。某个“状态”下的“值”,就是这个状态所对应的子问题的解。
例:在数字三角形中 MaxSum(i,j)的一组取值(i , j)即为一个状态
同时,每个状态的值仅需计算一次,我们要用一个数组将所有计算的值储存起来
3. 确定初始状态(边界条件)
以数字三角形为例,初始状态就是底边数字,值就是底边数字。
4.确定状态转移转移方程
即如何一个或多个“值”已知的状态,求出另一个“状态”的值。
数字三角形的状态转移方程:
MaxSum[r][j] = { D [i] [j] ( 当 r = N ,边界情况) } 或 { Max ( MaxSum[ r + 1 ] [ j ] , MaxSum[ r + 1 ] [ j + 1] ) + D [ r ] [ j ] (其他情况) }
由递归到递推
#include <iostream> #define MAX 101 int N; int D[MAX][MAX]; int Max[MAX][MAX]; void CountMax() { for(int i = 1;i <= N;++i ) Max [i] = D [i]; for(int i = N-1;i >= 1; --i) for(int j = 1; j <= i ; ++j) { int a = Max[i+1][j]; int b = Max[i+1][j+1]; int m = a > b ? a : b; Max[i][j] = m + D[i][j]; } } using namespace std; int main() { cin >> N; for(int i = 1; i <= N; ++i) for(int j = 1; j <= i;++j) cin >> D[i][j]; CountMax(); cout << Max[1][1] << endl; return 0; }
相关文章推荐
- 动态规划解题的一般思路
- 动态规划解题的一般思路
- 利用动态规划解题思路
- sdut.acm 2012级《程序设计基础Ⅱ)》_动态规划 数字三角形问题
- 图形数字的识别算法: 车牌识别及验证码识别的一般思路
- [Python]数字在排序数组中出现的次数 解题思路
- 动态规划__数字三角形
- 动态规划--再论(数字三角形 poj1163)
- poj 1163数字三角形问题--动态规划
- 数字三角形_递归_递推(动态规划)
- leetCode 18.4Sum (4数字和) 解题思路和方法
- 数字三角形(版本I-III)[动态规划]
- poj3176Cow Bowling,数字三角形,动态规划
- 数字三角形升级版(棋盘型动态规划)
- 动态规划解决数字塔-数字三角形问题
- 【动态规划】数字三角形最大值(一)(递归)
- 动态规划,背包问题的解题思路
- POJ_3176_Cow_Bowling_(数字三角形)_(动态规划)
- leetCode 7. Reverse Integer (数字反转) 解题思路与方法
- 动态规划--数字三角形