哈哈,又一道动归。
这道题,状态可以设成两个:一个是从左上方得到的最大值,另一个是从右上方得到的最大值,状态转移也分开求。
设f(l, i, j, k)表示状态,其中f(0, i, j, k)表示当前(i, j)点从左上方得到的经过k个负数格子的最大值,状态转移方程为:
f(0, i, j, k,) = max(f(0, i - 1, j, k + t), f(1, i - 1, j, k + t), f(0, i, j - 1, k + t)) + grid[i][j],若当前格子的值为正,t=0,否则为-1;同理可以写出f(1, i, j, k)的状态转移方程。
我写的时候怕格子里的数据太大,用了LL,先把每个格子初始化为负无穷大。若为负无穷大,则值还没求,若为负无穷大+1,则无解。
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define LL long long
const LL INF = (LL)1 << 60;
int n, K;
LL f[2][80][80][10], grid[80][80];
int mod(int x)
{
return (x + 2) % 2;
}
LL dp(int i, int j, int k, int l)
{
if(i < 1 || j < 1 || k < 0) {
//printf("*%d %d %d %d %lld\n", i, j, k, l, -INF + 1);
return -INF + 1;
}
LL &ans = f[l][i][j][k];
if(ans != -INF) return ans;
int t = 0;
ans = -INF + 1;
if(grid[i][j] < 0) t = -1;
if(i > 0)
ans = max(dp(i - 1, j, k + t, l), dp(i - 1, j, k + t, mod(l - 1)));
if(l == 0){
if(j > 1) ans = max(ans, dp(i, j - 1, k + t, l));
}else{
if(j < n) ans = max(ans, dp(i, j + 1, k + t, l));
}
if(ans != -INF + 1){
ans += grid[i][j];
}
//printf("%d %d %d %d %lld\n", i, j, k, l, f[l][i][j][k]);
return ans;
}
int main()
{
//freopen("input.txt", "r", stdin);
int con = 1;
while(scanf("%d %d", &n, &K) == 2){
if(!n && !K) break;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
scanf("%lld", &grid[i][j]);
for(int i = 0; i <= n; i++)
for(int j = 0; j <= n; j++)
for(int k = 0; k <= K; k++)
f[0][i][j][k] = f[1][i][j][k] = -INF;
if(grid[1][1] >= 0)
f[0][1][1][0] = f[1][1][1][0] = grid[1][1];
else if(K >= 1)
f[0][1][1][1] = f[1][1][1][1] = grid[1][1];
LL ans = -INF;
for(int i = 0; i <= K; i++){
ans = max(ans, dp(n, n, i, 0));
}
if(ans != -INF + 1){
printf("Case %d: %lld\n", con++, ans);
}else printf("Case %d: impossible\n", con++);
}
return 0;
}