数字三角形
 
 
描述: 
          有一个由非负整数组成的三角形,第一行只有一个数,除了最下行之外每个数的左下方和右下方各有一个数。
 
 问题:    
          从第一行的数开始,每次可以往左下或右下走一格,直到走到最下行,把沿途经过的数全部加起来。如何走才能使得这个和尽量大? 
 
 
分析:
       不难看出此题是一个动态的决策问题:每次有两种选择——左下或右下。如果用回溯法求出所有的可能的路线,就可以从中选出最优的路线。但和往常一样,回溯法的效率太低:一个n层数字三角形的完整路线有2^n条,当n很大时回溯法的速度将让人无法忍受。因此本题讨论用递归,递推及记忆化搜索的方法实现,虽然还有其他的方法,但此时只讨论学习比较相似的这几种方法。
 
 
 
最先想到的是递归实现:
#include "stdio.h"
#define maxn 100
int a[maxn][maxn],n;inline max(int x,int y)
{return x>y?x:y;	
}//递归计算实现 
int d(int x,int y)
{return a[x][y]+(x==n?0:max(d(x+1,y),d(x+1,y+1)));
}int main()
{while(~scanf("%d",&n))	{int i,j;for(i=1;i<=n;i++){for(j=1;j<=i;j++)scanf("%d",&a[i][j]);}printf("max:%d\n",d(1,1));}return 0;
}例: 在下列计算中d(3,2)被重复调用
                    d(2,1)   的计算会调用--> d(3,1) , d(3,2)
                     d(2,2)   的计算会调用--> d(3,2) , d(3,3) 
 
 
 
递推的实现:
#include "stdio.h"
#define maxn 100
int a[maxn][maxn],n;inline max(int x,int y)
{return x>y?x:y;	
}//递推实现 
int d(int x,int y)
{int d[n][n],i,j; for(j=1;j<=n;j++) d[n][j]=a[n][j];for(i=n-1;i>=1;i--){for(j=1;j<=i;j++)d[i][j]=a[i][j]+max(d[i+1][j],d[i+1][j+1]);}return d[x][y];
} int main()
{while(~scanf("%d",&n))	{int i,j;for(i=1;i<=n;i++){for(j=1;j<=i;j++)scanf("%d",&a[i][j]);}				printf("max:%d\n",d(1,1));							}return 0;
}
 
记忆化搜索实现:
#include "stdio.h"
#include "string.h" 
#define maxn 100
int a[maxn][maxn],n;
int d[maxn][maxn];	//记忆化搜索所使用的状态记忆数组 
inline max(int x,int y)
{return x>y?x:y;	
}/*记忆话搜索。程序分成两部分。首先  memset(d,-1,sizeof(d)); 把d全部初始化为-1, 
然后编写递归函数: 
*/ 
int distance(int i,int j)
{if(d[i][j]>=0) return d[i][j];return d[i][j]=a[i][j]+(i==n?0:max(distance(i+1,j),distance(i+1,j+1)));
} 
/*上述程序依然是递归的,但同时也把计算结果保存在数组d中。题目中说各个数都是非负的,因此
如果已经计算过某个d[i][j],则它应是非负的,这样,只需把所有d初始化为-1,即可通过判断是否
d[i][j]>=0得知是否已经被计算过。 
*/ int main()
{while(~scanf("%d",&n))	{int i,j;for(i=1;i<=n;i++){for(j=1;j<=i;j++)scanf("%d",&a[i][j]);}memset(d,-1,sizeof(d));	//状态记忆化数组初始化 				printf("max:%d\n",distance(1,1));							}return 0;
}