链接http://acm.hdu.edu.cn/showproblem.php?pid=4826
【题目】
Problem Description
度度熊是一只喜欢探险的熊,一次偶然落进了一个m*n矩阵的迷宫,该迷宫只能从矩阵左上角第一个方格开始走,只有走到右上角的第一个格子才算走出迷宫,每一次只能走一格,且只能向上向下向右走以前没有走过的格子,每一个格子中都有一些金币(或正或负,有可能遇到强盗拦路抢劫, 度度熊身上金币可以为负,需要给强盗写欠条),度度熊刚开始时身上金币数为0,问度度熊走出迷宫时候身上最多有多少金币?
Input
输入的第一行是一个整数T(T < 200),表示共有T组数据。 每组数据的第一行输入两个正整数m,n(m<=100,n<=100)。接下来的m行,每行n个整数,分别代表相应格子中能得到金币的数量,每个整数都大于等于-100且小于等于100。
Output
对于每组数据,首先需要输出单独一行”Case #?:”,其中问号处应填入当前的数据组数,组数从1开始计算。 每组测试数据输出一行,输出一个整数,代表根据最优的打法,你走到右上角时可以获得的最大金币数目。
Sample Input
2 3 4 1 -1 1 0 2 -2 4 2 3 5 1 -90 2 2 1 1 1 1
Sample Output
Case #1: 18 Case #2: 4
【分析】和经典的从左上角走到右下角取最大值类似的,只是可以往三个方向走,导致DP顺序有点难找;
首先,对于往右走这个方向,由于不能往左走,所以只要按照列(j)从左往右DP就能保证先算出左边的DP;对于第一列,只能往下走,所以直接初始化,DP从第二列开始。
主要难点是对与可以上下两个方向走动的,对于行(i)的顺序是先算上还是先算下面的,都不行。这时需要在DP过程中,分别算出左边的,上面的和下面的只,选择最大的那个,
然后在计算上下两个方向的时候要每次都和左边过来的值比较选择最优的一个,得到最优值只要比较上下两个方向的最大值就可以了,具体看代码。
【AC代码】46ms
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 110
int a[MAXN][MAXN], dp[MAXN][MAXN], h, w;
int main ()
{
#ifdef SHY
freopen("e:\\1.txt","r",stdin);
#endif
int t, count = 0;
scanf ("%d%*c" ,&t);
while(t--)
{
scanf ("%d %d%*c", &h, &w);
for (int i = 1; i <= h; i++)
{
for (int j = 1; j <= w; j++)
scanf ("%d%*c", &a[i][j]);
}
memset(dp,-0x3f,sizeof(dp));
dp[0][1] = 0;
for (int i = 1; i <= h; i++)
dp[i][1] = a[i][1]+dp[i-1][1];
int buf[MAXN][2];//buf[][1]记录从上方走来的当前最大价值,buf[][1]记录下方走来的当前最大价值
for (int j = 2; j <= w; j++)
{
for (int i = 1; i <= h; i++)//记录从左边走来的价值
dp[i][j] = dp[i][j-1]+a[i][j];
buf[1][0] = dp[1][j];
for (int i = 2; i <= h; i++)//每次都要比较从左边还是上面走来大
buf[i][0] = max(dp[i][j],buf[i-1][0]+a[i][j]);
buf[h][1] = dp[h][j];
for (int i = h-1; i >= 1; i--)//每次都要比较从左边还是下面走来大
buf[i][1] = max(dp[i][j],buf[i+1][1]+a[i][j]);
for (int i = 1; i <= h; i++)//找到最大值
dp[i][j] = max(buf[i][0],buf[i][1]);
}
printf ("Case #%d:\n%d\n", ++count, dp[1][w]);
}
return 0;
}