Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 29544 | Accepted: 17414 |
Description
7 3 8 8 1 0 2 7 4 4 4 5 2 6 5 (Figure 1)
Input
Output
Sample Input
5 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5
Sample Output
30
Source
题目大意: 给定一个由数字组成的三角形,第i行有i个数字,每个数字可以到达它的左下和右下的数字。求从第一个数字出发到最后一行,把沿途的数字加起来,求总和最大的走法。
初看这题,似乎可以用回溯的方法求出每一条可能的路线,然后比较总和。但是需要注意的是,这种算法的复杂度为2^n,n为行数。这样的耗时显然是不成立的。
动态规划的算法设计可以分为下面4个步骤:
第一、描述最优解的结构
第二、递归定义最优解的值
第三、按自底向上的方式计算最优解的值
第四、由计算出的结果构造一个最优解的值
(这四个步骤摘自《算法导论》)
首先,最优解的结构我们已经清楚了,即一条路径,从第一层到最后一层。现在要做的是递归定义最优解的值,在此,我们先定义一个状态max[i][j]表示从1,1出发到达(i,j)的最大路径,那么max[n][j]就是最后一行的第j列的最大值,遍历第n行的n个值取最大值即是我们要求的答案。其次是递归定义最优解的值,在这里我们需要注意的是DP问题中的效率问题,尽量少用递归求解。因为递归是一个耗时的事情, 它进行了很多重复的搜索,比如每次求一个值,它都会重复计算上一个值的子问题,所以,最好的解决办法就是记忆化搜索。
注意到(i,j)只能由(i-1,j)和(i-1,j-1),j = 2…… 则状态方程: max[i][j] = max {max[i-1][j]+input[i][j],max[i-1][j-1]+input[i][j]}
除了第一行和第二行需要初始化处理(事实上第二行也不需要额外处理),后续的都可以记忆化搜索的方式解出。
到计算到第n行时,遍历取最大值即可。
代码如下:
/*
Memory: 480 KB Time: 16 MS
Language: GCC Result: Accepted
This source is shared by hust_lcl
*/
#include <stdio.h>
#include <stdlib.h>
int input[110][110];
int max[110][110];
int main()
{
int n , i , j;
int p , q , flag;
scanf("%d",&n);
for(i = 1 ; i <= n ; i ++)
for(j = 1 ; j <= i ; j ++)
scanf("%d",&input[i][j]);
max[1][1] = input[1][1];
max[2][1] = input[1][1] + input[2][1];
max[2][2] = input[1][1] + input[2][2];
for(i = 2 ; i <= n ; i ++)
for(j = 1 ; j <= i ; j ++)
{
p = max[i-1][j-1] + input[i][j];
q = max[i-1][j] + input[i][j];
if(p > q)
max[i][j] = p;
else
max[i][j] = q;
}
flag = max[n][1];
for(i = 2 ; i <=n ; i ++)
if(max[n][i] > flag) flag = max[n][i];
printf("%d",flag);
return 0;
}