DP问题之数塔问题

问题描述:考虑下面的数字金字塔,写一个程序来计算从最高点开始,在底部任意处结束经过的数字和最大,每一步可以走到左下和右下的点。

  7

3   8

    8   1   0

  2   7   4   4

4   5   2   6   5

变形后:

7

3   8

8   1   0

2   7   4   4

4   5   2   6   5

问题分析:可以先对金字塔进行变形,如上。对于数字金字塔可以用(i, j)来表示数字在金字塔中的位置。

对于金字塔中间的一点,想要经过它,则必须经过它的左上或上面的点(变形后)。因此要使经过该点的和最大,则是在经过左上和右上的点中较大的“最大和”,

然后加上该点的值。这样,状态很明显是金字塔的层。

设计一个二维状态opt[i, j]表示到第 i 行 第 j 列的最大和,那么决策就是opt[i-1, j-1]和opt[i-1, j]中较大值在加上(i, j)处的值。

状态转移方程为:

opt[i, j] = opt[i-1, j] + a[i, j]  j=1 //左边缘

opt[i, j] = opt[i-1, j-1] + a[i, j]   j=i //右边缘

opt[i, j] = max{opt[i-1, j-1], opt[i-1, j]} + a[i, j];  1<j<i

最后的答案:ans = max{opt[n, i]}  1 <= i <= n;

还有一点就是:其实从上往下和从下往上走是一样的,从下往上走时,可以省下求最大值的步骤,最终结果就是opt[1,1],状态方程为:

opt[i, j] = max{opt[i+1, j], opt[i+1, j+1]} + a[i, j] ;

测试代码:

#include <stdio.h> 
#include
<stdlib.h> 

int main()
{
int a[5][5] = {
{
7, 0, 0, 0, 0},
{
3, 8, 0, 0, 0},
{
8, 1, 0, 0, 0},
{
2, 7, 4, 4, 0},
{
4, 5, 2, 6, 5}
};
int opt[5][5];
int i, j;

for(i=0; i<5; i++)
opt[
4][i] = a[4][i];//初始化最后一行

for(i=3; i>=0; i--)
for(j=0; j<=i; j++)
if(opt[i+1][j]>opt[i+1][j+1])
opt[i][j]
= opt[i+1][j] + a[i][j];
else
opt[i][j]
= opt[i+1][j+1] + a[i][j];

printf(
"max:%d\n", opt[0][0]);
return0;
}
posted @ 2011-04-30 15:26  steel_heart  阅读(439)  评论(0编辑  收藏  举报