问题:将一个由n行数字组成的三角形,如图所示,设计一个算法,计算出三角形的由顶至底的一条路径,使该路径经过的数字总和最大?
图1数字三角形
首先拿到这题,我们第一想法肯定是使用深度优先算法DFS或者广度优先算法BFS,但是使用DFS算法需要遍历所有点,搜索时会重复遍历一些点,导致时间成指数增长。
图2重复计算的次数
设我们用二维数组map[m][n]来存储数字三角形,把图1转换成实际的存储格式:
图3数字三角形存储格式
如图2和图3所示,我们通过观察可以发现,三角形的边界位置如3、8、2 、4等的位置只需要计算一次,而中间的位置数字例如1的计算次数为上一行3的次数加上8的次数,则次数计算公式为:
map[i][j]=map[i-1][j-1]+map[i-1][j] ( i>0&&i<=m,j>0&&j<=n )
map[i][j]来表示第i行第j个数字( i和j都是从1开始),现在来使用DFS分析可以发现,从map[i][j]出发,我们可以选择的下一条路径只有map[i+1][j]或者map[i+1][j+1],因此递推公式可以描述为
max(i,j)=max ( max(i+1,j) , max(i+1,i+1) ) +map[i][j]
图4递归遍历方式
程序结果:
#include <iostream>
#define MAX 10
using namespace std;
int map[MAX][MAX];
int n;
int Max(int i, int j) {
if (i == n)
return map[i][j];
int x = Max(i + 1, j);
int y = Max(i + 1, j + 1);
return (x>y?x:y) + map[i][j];
}
int main() {
int i, j;
cin >> n;
for (i = 1; i <= n; i++)
for (j = 1; j <= i; j++)
cin >> map[i][j];
cout << Max(1, 1) << endl;
system("pause");
}
虽然使用递归可以解决问题,但是效率不高,想要提高效率就需要数字三角形中的每个数字只需要计算一遍即可,我们使用f[i][j]来保存表示从顶点(1, 1)到顶点(i, j)的最大值。
通过对上面的递归地分析可得状态转移函数为:
f[i][j]=max(f[i+1][j],f[i+1][j+1])+map[i][j]
程序结果如下:
#include<iostream>
#define size 20
using namespace std;
int main() {
int map[size][size];
int n;
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= i; j++)
cin >> map[i][j];
for (int i = n - 1; i > 0; i--)
for (int j = 1; j <= i; j++)
map[i][j] += (map[i + 1][j] > map[i + 1][j + 1] ? map[i + 1][j] : map[i + 1][j + 1]) ;
cout << map[1][1] << endl;
system("pause");
return 0;
}