做题感悟:开始不用标记数组把 dp 数组初始化一下用于标记但是这样因为初始化的原因就超时了,改为标记数组才过。
解题思路:记忆化搜索
这题很明显,如果用递推的方法的话必定不好写,因为在一行里可以向左做可以向右走,这样就导致不好递推,如果用记忆化方法的话就很好写了,如果单纯的只向右和下的话可以用三维标记 dp[ i ] [ j ] [ k ] (代表到达第i行第 j 列经历了 k 个负数所得到的最优值) ,这里还可以向左走,so~> 必须必须加一维方向,标记是从哪个方向得到的,放置重复走 ,即: dp [ i ] [ j ] [ k ] [ d ] 代表到达第 i 行第 j 列出现了 k 个负数从方向 d 得到的最优解。
代码:
#include<iostream>
#include<ctime>
#include<fstream>
#include<sstream>
#include<stack>
#include<cstring>
#include<map>
#include<vector>
#include<cstdio>
#include<algorithm>
#define INT long long int
using namespace std ;
const INT INF = 999999999 ;
const INT MY = 10 ;
const INT MX = 75 + 5 ;
INT n ,k ;
bool vis[MX][MX][MY][MY] ;
INT dp[MX][MX][MY][MY] ,g[MX][MX] ;
INT dx[4]={1 ,0 ,0} ,dy[4]={0 ,-1 ,1} ;
void input()
{
for(INT i = 0 ;i < n ;i++)
for(INT j = 0 ;j < n ;j++)
scanf("%lld",&g[i][j]) ;
memset(vis ,false ,sizeof(vis)) ;
}
INT DP_Memory(INT x ,INT y ,INT z ,INT d)
{
if(z > k) return -INF ;
if(x==n-1 && y == n-1)
return g[x][y] ;
if(vis[x][y][z][d])
return dp[x][y][z][d] ;
vis[x][y][z][d] = true ;
INT& ans = dp[x][y][z][d] ;
ans = -INF ;
for(INT t = 0 ;t < 3 ;t++)
{
INT sx = x + dx[t] ;
INT sy = y + dy[t] ;
if(d + t == 3) continue ;
if(sx >= 0 && sy >= 0 && sx < n && sy < n)
{
INT mx = (g[sx][sy] < 0) ;
INT my = DP_Memory(sx ,sy ,z+mx ,t) ;
if(my != -INF)
ans = max(my + g[x][y] ,ans) ;
}
}
return ans ;
}
int main()
{
int cse = 1 ;
while(scanf("%lld%lld",&n ,&k) ,n+k)
{
input() ;
INT x = (g[0][0] < 0) ,ans = -INF ;
ans = DP_Memory(0 ,0 ,x ,0) ;
cout<<"Case "<<cse++<<": " ;
if(ans != -INF)
cout<<ans<<endl ;
else cout<<"impossible"<<endl ;
}
return 0 ;
}