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;
}
#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;
}