总时间限制: 1000ms 内存限制: 65536kB

描述

  1. 7
  2. 3 8
  3. 8 1 0
  4. 2 7 4 4
  5. 4 5 2 6 5
  6. (Figure 1)

Figure 1 shows a number triangle. Write a program that calculates the highest sum of numbers passed on a route that starts at the top and ends somewhere on the base. Each step can go either diagonally down to the left or diagonally down to the right.

输入

Your program is to read from standard input. The first line contains one integer N: the number of rows in the triangle. The following N lines describe the data of the triangle. The number of rows in the triangle is > 1 but ≤ 100. The numbers in the triangle, all integers, are between 0 and 99.

输出

Your program is to write to standard output. The highest sum is written as an integer.

样例输入

  1. 5
  2. 7
  3. 3 8
  4. 8 1 0
  5. 2 7 4 4
  6. 4 5 2 6 5

样例输出

  1. 30

思路

初始解法

用二维数组存放数字三角形.
D(r, j): 第r行第j个数字(r, j从1开始计数)
MaxSum(r, j): 从D(r, j)到底边的各个路径中, 最佳路径的数字之和.

问题: 求MaxSum(1, 1)

这是典型的递归问题:
从D(r, j)出发, 下一步只能走 D(r+1, j) 或 D(r+1, j+1). 故对于N行三角形来说:

  1. if( r == N )
  2. MaxSum(r, j) = D(r, j);
  3. else
  4. MaxSum(r, j) = Max( MaxSum(r+1, j), MaxSum(r+1, j+1) ) + D(r, j);

有了上面的递归式子, 我们可以写出初始的解法:

  1. // Using C99 Standard
  2. #include <stdio.h>
  3. #define MAX 100
  4. int triangle[MAX][MAX];
  5. int number;
  6. int MaxSum(int i, int j) {
  7. if( i == number )
  8. return triangle[i][j];
  9. int x = MaxSum(i+1, j);
  10. int y = MaxSum(i+1, j+1);
  11. int maxBetweenXY = x > y ? x : y;
  12. return (maxBetweenXY + triangle[i][j]);
  13. }
  14. int main() {
  15. scanf("%d", &number);
  16. for(int i = 1; i <= number; i++) {
  17. for(int j = 1; j <= i; j++) {
  18. scanf("%d", &triangle[i][j]);
  19. }
  20. }
  21. printf("%d\n", MaxSum(1, 1));
  22. return 0;
  23. }

代码结果是对的, 但是POJ不通过, 原因是超时了.

该算法包含了很多重复计算:

  1. 第一行调用了1次MaxSum
  2. 第二行调用了2次MaxSum
  3. 第三行调用了4次MaxSum
  4. 第四行调用了8次MaxSum
  5. 第五行调用了16次MaxSum

这时间复杂度是 2 , 对于比较大的测试点, 肯定超时.

我们怎么改进?

每算一次MaxSum就保存起来, 下次要算的时候就直接拿出来用. 这样的时间复杂度是O(n), 因为三角形数字总数是: n(n+1) / 2.

记忆递归型动归程序

  1. // Using C99 Standard
  2. #include <stdio.h>
  3. #define MAX 100
  4. int triangle[MAX][MAX];
  5. int mysum[MAX][MAX];
  6. int number;
  7. int MaxSum(int i, int j) {
  8. if( mysum[i][j] != -1 )
  9. return mysum[i][j];
  10. if( i == number )
  11. mysum[i][j] = triangle[i][j];
  12. int x = MaxSum(i+1, j);
  13. int y = MaxSum(i+1, j+1);
  14. int maxBetweenXY = x > y ? x : y;
  15. mysum[i][j] = maxBetweenXY + triangle[i][j];
  16. return mysum[i][j];
  17. }
  18. int main() {
  19. scanf("%d", &number);
  20. for(int i = 1; i <= number; i++) {
  21. for(int j = 1; j <= i; j++) {
  22. scanf("%d", &triangle[i][j]);
  23. mysum[i][j] = -1;
  24. }
  25. }
  26. printf("%d\n", MaxSum(1, 1));
  27. return 0;
  28. }

转递归为递推

我们从底层往上算,算到D(1, 1)。由于底层的每个元素到底边最大距离就是它自己,这是我们已知的。顶层D(1, 1)到底边的最大距离是未知的,由已知推出未知,是谓递推。
递推公式如下:

POJ 1163 The Triangle - 图1

程序代码如下:

  1. // Using C99 Standard
  2. #include <stdio.h>
  3. #define MAX 100
  4. int triangle[MAX][MAX];
  5. int mysum[MAX][MAX];
  6. int number;
  7. int main() {
  8. scanf("%d", &number);
  9. for(int i = 1; i <= number; i++) {
  10. for(int j = 1; j <= i; j++) {
  11. scanf("%d", &triangle[i][j]);
  12. }
  13. }
  14. for(int j = 1; j <= number; j++)
  15. mysum[number][j] = triangle[number][j];
  16. for(int i = number; i > 1; i--) {
  17. for(int j = 1; j < i; j++) {
  18. if(mysum[i][j] > mysum[i][j+1])
  19. mysum[i-1][j] = mysum[i][j] + triangle[i-1][j];
  20. else
  21. mysum[i-1][j] = mysum[i][j+1] + triangle[i-1][j];
  22. }
  23. }
  24. printf("%d\n", mysum[1][1]);
  25. return 0;
  26. }