有一个数字三角形,现从顶点出发,在每个节点可以选择向左走或者向右走,一直走到底层。现设计一种算法,计算出从三角形顶端到底部的一条路径,是该路径经过的数字总和最小。
Dev_c++
此问题采用动态规划思想解决。
数字三角形采用一个二维数组init[][]存储,第一行的数字存在init[0][0],第二行的数字存在init[1][0]和init[1][1],第三行的数字存在init[2][0]、init[2][1]和init[2][2],所以第i行的数字存储在数组init[i-1][0]至init[i-1][i-1]。
现计算路径的数字总和status[][],由题意知,要到达第三行的数字必须经过第二行和第一行,即要到达init[2][0]的总和status[2][0]则需计算到达init[1][0]的数字总和status[1][0],要到达init[2][1]则需计算min{到达init[1][0]的数字总和status[1][0],到达init[1][1]的数字总和status[1][1]},要到达init[2][2]则需计算到达init[1][1]的数字总和status[1][1]。
由此可知若要到达init[k][0]的元素只需计算到达init[k-1][0]的数字总和status[k-1][0],
要到达init[k][k]的元素只需计算到达init[k-1][k-1]的数字总和status[k-1][k-1],
要到达init[2…k-1][1…k-1]的元素则需计算min{到达init[1…k-2][0…k-1]的数字总和status[1…k-2][0…k-1],到达init[1…k-2][1…k-1]的数字总和status[1…k-2][1…k-1]}。
递归求解方案
初始化status[][]数组时赋值10000,而构成数字三角形的数值都不会超过100且规定最大行数为50,即max{status[k][k]}<10000,由此要到达init[k][k]的元素就需计算min{到达init[k-1][k-1]的数字总和status[k-1][k-1],到达init[k-1][k]的数字总和status[k-1][k]}。再由(1)总结得
status[i][j]=status[i-1][0]+init[i][0] ( j=0 )
or min{status[i-1][j-1],status[i-1][j]}+init[i][j] ( j!=0)
计算最优代价
根据计算status[i][j]的递归式,可以写出一个迭代的算法进行计算,并在计算过程中保存已解决的子问题答案,每个子问题计算一遍,在后面需要时只需简单地进行查表,其中Road[][]记录路径信息。
算法如下:
Algorithms countStatus(init[][],status[][],Road[][],num)
For i 0 to num //行数
For j 0 to i //列数
If i==0&&j==0
status[i][j] = init[i][j]
else if i!=0&&j==0 //到达init[k][0]的情况
status[i][j] = status[i-1][j]+init[i][j];
Road[2*i-1][j] = ‘|‘
Else if status[i-1][j]>status[i-1][j-1] //到达init[k][k]
status[i][j] = init[i][j]+status[i-1][j-1];
Road[2*i-1][2*j-1] = ‘\\‘
Else
status[i][j] = init[i][j]+status[i-1][j];
Road[2*i-1][2*j] = ‘|‘
构造最优解
找出min(status[i][0…i]),假设是status[i][j],再根据status[i-1][j-1]和status[i-1][j]判断status[i][j]是从左边来的还是从右边来的,将方向信息记录在Road_sign[][]中,结束后只输出到达status[i][j]的路径。
Algorithms path(status[][],Road_sign[][])
min = status[num-1][0]
k=0;
for j=1 to num
if(min>status[num-1][j])
min = status[num-1][j]; //最小的代价
k=j;
for i=num-1 to 0
if(k == 0) //若最小的元素是status[i][0]
Road_sign[2*i-1][0] = 1; //输出标记
else if(k == i) //若最小的元素是status[i][i]
Road_sign[2*i-1][2*i-1] = 1;
k = k-1; //通过init[i-1][i-1]到达
else //若最小的元素是status[i][1…i-1]
if(status[i-1][k]>status[i-1][k-1])
Road_sign[2*i-1][2*k-1] = 1;
k = k-1; //通过init[i-1][i-1]到达
else
Road_sign[2*i-1][2*k] = 1;
For i=0 to 2*num-1 //输出最优路径
For j=0 to i
if(i%2 == 0)
printf("%3c",Road[i][j]);
else
if(Road_sign[i][j] == 1)
printf("%3c",Road[i][j]);
c语言代码描述
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(int argc, char *argv[]) { int init[50][50],status[50][50]; char Road[100][100]; int Road_sign[100][100]; int i,j,k,num,min; printf("请输入三角形的层数:"); scanf("%d",&num); for(i=0; i<num; i++) { for(j=0; j<num; j++) { status[i][j] = 10000; } } //产生随机数 srand((unsigned)time(NULL)); for(i=0; i<num; i++) { for(j=0; j<=i; j++) { init[i][j] = rand()%100; } } //初始化方向信息数组 for(i=0; i<2*num; i++) { for(j=0; j<2*num; j++) { if(i%2==0 && j%2==0) { Road[i][j] = ‘o‘; } else { Road[i][j] = ‘ ‘; } Road_sign[i][j]=0; } } //输出数值三角形 for(i=0; i<num; i++) { for(j=0; j<=i; j++) { printf("%4d",init[i][j]); } printf("\n"); } printf("\n\n"); //计算出从三角形顶端走到底部的最小值,以及保存路径信息 for(i=0; i<num; i++) { for(j=0; j<=i; j++) { if(i==0 && j==0) { status[i][j] = init[i][j]; //到达顶点 } else if(j==0 && i!=0) { //到达init[k][0]的情况 status[i][j] = status[i-1][j]+init[i][j]; Road[2*i-1][j] = ‘|‘; //只能通过init[k-1][0]到达 } else if(status[i-1][j]>status[i-1][j-1]) { //到达init[k][k]的情况 status[i][j] = init[i][j]+status[i-1][j-1]; Road[2*i-1][2*j-1] = ‘\\‘; //通过init[k-1][k-1]到达 } else { status[i][j] = init[i][j]+status[i-1][j]; Road[2*i-1][2*j] = ‘|‘; //通过init[k-1][k]到达 } } } //构造最优解 min = status[num-1][0]; k=0; for(j=1; j<num; j++) { if(min>status[num-1][j]) { min = status[num-1][j]; //确定最优解 k=j; } } //标记要输出的最优路径 for(i=num-1; i>0; i--) { if(k == 0) { Road_sign[2*i-1][0] = 1; } else if(k == i) { Road_sign[2*i-1][2*i-1] = 1; k = k-1; //通过左上方到达 } else { if(status[i-1][k]>status[i-1][k-1]) { Road_sign[2*i-1][2*k-1] = 1; k = k-1; //通过左上方到达 } else { Road_sign[2*i-1][2*k] = 1; //通过上方到达 } } } //输出到达各点的代价 for(i=0; i<num; i++) { for(j=0; j<=i; j++) { printf("%4d",status[i][j]); } printf("\n"); } printf("\n"); /* for(i=0; i<2*num-1; i++) { for(j=0; j<=i; j++) { printf("%2d",Road_sign[i][j]); } printf("\n\n"); } */ //输出最优路径 for(i=0; i<2*num-1; i++) { for(j=0; j<=i; j++) { if(i%2 == 0) { printf("%3c",Road[i][j]); } else { if(Road_sign[i][j] == 1) { printf("%3c",Road[i][j]); } else { printf(" "); } } } printf("\n"); } return 0; }
整个程序的计算量主要集中在算法countStatus()中的i 和 j 的两重循环中。循环体内的计算量为O(1),而2重循环的总次数为O(n2)。因此,该算法的计算时间上界为O(n2)。
Status[][]数组
最优的路径
原文:http://www.cnblogs.com/sillypasserby/p/5093982.html