一、问题描述
给出一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上的数加起来可以得到一个和,和最大的路径称为最佳路径。你的任务是求出最佳路径上的数字之和。注意:路径上的每一步只能从一个数走到下一层上和它最近的左边或者右边的数。
二、输入数据
输入的第一行是一个整数N(1<N<=100),给出三角形的行数。下面的N行给出数字三角形。数字三角形上的数的范围都在0~100之间。
三、输出要求
输出最大的和
四、输入样例
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
五、输出样例
30
六、解题思路
这道题可以用递归的方法解决。以D(r,j)表示第r行第j个数字,以MaxSum(r,j)表示从第r行的第j个数字到底边的最佳路径的数字之和,则本题是要求MaxSum(1,1)。
从某个D(r,j)出发,显然下一步只能走D(r+1,j)或者D(r+1,j+1)。如果走D(r+1,j)。那么得到的MaxSum(r,j)就是MaxSum(r+1,j)+D(r,j);如果走D(r+1,j+1),那么得到的MaxSum(r,j)就是MaxSum(r+1,j+1)+D(r,j)。所以,选择往哪里走,就看MaxSum(r+1,j)那个更大。为了提高效率,可以用一个二维数组aMaxSum[N][N]来存放计算出来的MaxSum值,下次需要MaxSum的值时直接取对应aMaxSum的值即可,避免重复计算。
#include<stdio.h> #include<memory.h> #define MAX_NUM 100 int D[MAX_NUM + 10][MAX_NUM + 10]; int N; int aMaxSum[MAX_NUM + 10][MAX_NUM + 10]; int MaxSum(int r, int j) { if(r == N) return D[r][j]; if(aMaxSum[r + 1][j] == -1) aMaxSum[r + 1][j] = MaxSum(r + 1, j); if(aMaxSum[r + 1][j + 1] == -1) aMaxSum[r + 1][j + 1] = MaxSum(r + 1, j + 1); if(aMaxSum[r + 1][j] > aMaxSum[r + 1][j + 1]) return aMaxSum[r + 1][j] + D[r][j]; return aMaxSum[r + 1][j + 1] + D[r][j]; } int main() { int m; scanf("%d", &N); memset(aMaxSum, -1, sizeof(aMaxSum)); for(int i = 0; i <= N; i++) { for(int j = 1; j <= i; j++) { scanf("%d", &D[i][j]); } } printf("%d", MaxSum(1, 1)); return 0; }这种讲一个问题分解为子问题递归求解,并且将中间结果保存避免重复计算的办法就叫做动态规划。动态规划通常用来求最优解,能用动态规划解决的求最优解问题,必须满足最优解的每个局部解也都是最优的。
实际上,递归的思想在编程时未必要实现为递归函数。在这道题中不需要写递归函数,从第n-1行元素开始向上递推,就能求得最终解。
#include<iostream> #include<cstdio> using namespace std; int main() { int n; int a[110][110]; cin >> n; for(int i = 0; i < n; i++) for(int j = 0; j <= i; j++) cin >> a[i][j]; for(int i = n - 1; i >= 0; i--) { for(int j = 0; j <= i; j++) { a[i][j] += a[i + 1][j] > a[i + 1][j + 1] ? a[i + 1][j] : a[i + 1][j + 1]; } } cout << a[0][0]; return 0; }