一、问题描述
★问题描述:给字一个由n行数字组成的数字三角形(等腰三角形)。试设计一个算法,计算出从三角形的顶至底的一条路径,使该路径经过的数字总和最大。
★算法设计:对于给定的由n行数字组成的数字三角形,计算从三角形的项至底的路径经过的数字和的最大值。
★数据输入:由文件input.txt提供输入数据。文件的第1行是数字三角形的计数n,1≤n≤100。接下来n行是数字三角形各行中的数字。所有数字在0~99之间。
★结果输出:将计算结果输出到文件output.txt。文件第1行中的数是计算出的最大值。
输入文件示例:input.txt
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出文件示例:output.txt
30
二、问题分析
(1).输入数据的储存和表示:
先将数字三角形由一个等腰变形为一个直角三角形,便于我们使用矩阵来保存和表示。
矩阵表示如下:
0 0 0 0 0 0
0 7 0 0 0 0
0 3 8 0 0 0
0 8 1 0 0 0
0 2 7 4 4 0
0 4 5 2 6 5
相应输入部分的代码:
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
cinfile>>NT[i][j];
}
for(int j=i+1;j<=n;j++){
NT[i][j]=0;
}
}
(2).解题思路
基本思路--采用自顶向下的方法
A.思路一:自顶向下依次遍历,在碰到分叉路的时候选择较大的数。(错误--局部最优并不等于整体最优)
比如上述例题按这种方法得到的“最优解”:7+8+1+7+5=27
而实际上的最优解:7+3+8+7+5=30
B.思路二:自顶向下依次遍历,列举出所有路径的大小,最后再进行比较。(正确)
保存数据:首先,我们需要一个矩阵储存当前路径的大小,这里我们使用NT(number triangle)保存原来的数字三角形,使用ST(sum triangle)保存当前路径的大小。由于每个路径数据再往下发展的时候都有两种可能,因此这个矩阵的大小为n*2(n-1),这里的话我直接使用了2n。
算法设计:
初始化,ST[i][0]=0;ST[0][j]=0;ST[1][1]=NT[1][1]
自顶向下遍历
当前路径的和=上一层路径的和+当前数据;
r[1][1]=a[1][1];
for(int i=2;i<=n;i++){
int count=1;
for(int j=1;j<=i-1;j++){
r[i][count++]=r[i-1][j]+a[i][j];//每一个数据对应两条路径
r[i][count++]=r[i-1][j]+a[i][j+1];
}
}
遍历结束后,ST的数据存储情况
比如10就是路径:7+3的和;
15就是路径:7+8的和;
等等
回溯最优路径:
由于我们在计算最优路径时,用ST保存了所有当前路径的大小。因此我们只需要用最后的最优路径的数值依次往回减就可以求出最优路径了。由于我们的回溯是自底向上,路径的数据顺序是倒着的。因此这里使用了栈来储存路径数据,利用其后进先出的特点回溯出最优路径。
三、详细设计(从算法到程序)
#include<iostream>
#include<stack>
#include<fstream>
#include<sstream>
using namespace std;
//计算数字三角形最大路径的值
int Numtri(int n,int **a,int **r){
r[1][1]=a[1][1];
for(int i=2;i<=n;i++){
int count=1;
for(int j=1;j<=i-1;j++){
r[i][count++]=r[i-1][j]+a[i][j];
r[i][count++]=r[i-1][j]+a[i][j+1];
}
}
}
//回溯最优路径
int Traceback(int n,int posi,int **r,ofstream &outfile){
stack<int>trace; //建立一个堆栈,利用其后进先出的特点来打印出路径
int j=posi/2+1,c=r[n][posi];
for(int i=n-1;i>=0;i--){//从最后一层开始往上回溯
trace.push(c-r[i][j]);
c=r[i][j];
if(j%2==0) j=j/2;
else j=j/2+1;
}
while(!trace.empty()){
outfile<<trace.top()<<" ";
trace.pop();
}
}
int main(){
ifstream cinfile;
cinfile.open("input.txt",ios::in);
int n;
cinfile>>n;
int **NT=new int *[n+1]();
int **ST=new int *[n+1]();
for(int i=0;i<=n;i++)
{
NT[i]=new int[n+1];
ST[i]=new int[2*(n+1)];
}
for(int i=0;i<=n;i++){
NT[0][i]=0;
NT[i][0]=0;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
cinfile>>NT[i][j];
}
for(int j=i+1;j<=n;j++){
NT[i][j]=0;
}
}
for(int i=0;i<=n;i++)
for(int j=0;j<=2*n;j++) ST[i][j]=0;
cinfile.close();
ofstream outfile;
outfile.open("output.txt",ios::out);
Numtri(n,NT,ST);
int Max=ST[n][1],pos=1;
for(int i=2;i<=2*n;i++){
if(Max<ST[n][i]){
Max=ST[n][i];
pos=i;
}
}
outfile<<Max<<endl;
Traceback(n,pos,ST,outfile);
return 0;
}
/*
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
*/
四、程序运行结果
五、分析与总结
在做动态规划这一类问题时一定要注意很多时候局部最优解并不等于整体最优解。