2014-08-25 00:43:21
思路:本来想用递推式写的,写了半天还是WA,原因在于方向,这题和以前hdu写的走迷宫(只能右 / 下方向)凑最大点数那题不同,这题左右都可以走,而且又规定一个格子不能走两遍,所以普通递推DP是难以知道这个状态是从哪个方向转移来的,如果从右边转移来的,那么接下来右边就不能走了(左边同理)。
所以为了方便,这题直接用记忆化搜索解,dp[i][j][k][dir] 表示用[i,j]这个点到终点经过k次负权格子,且状态是从dir方向转移来的最大路径和。
(后来想想,如果在普通递推DP中加上方向维度,可能也可行,呵呵)
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <cmath> 5 using namespace std; 6 const int INF = 1 << 30; 7 typedef long long ll; 8 9 int n,k; 10 int g[80][80]; 11 ll dp[80][80][6][5]; 12 int caled[80][80][6][5]; 13 int vis[80][80]; 14 int Case = 0; 15 16 ll Dfs(int x,int y,int cnt,int dir){ 17 ll &res = dp[x][y][cnt][dir]; 18 if(g[x][y] < 0){ 19 cnt++; 20 if(cnt > k){ 21 return -INF; 22 } 23 } 24 if(x == 1 && y == 1){ 25 return res = g[x][y]; 26 } 27 if(caled[x][y][cnt][dir]){ 28 return res; 29 } 30 caled[x][y][cnt][dir] = 1; 31 res = -INF; 32 vis[x][y] = 1; 33 ll tem; 34 if(y > 1 && !vis[x][y - 1]){ 35 if((tem = Dfs(x,y - 1,cnt,4)) != -INF){ 36 res = max(res,tem + g[x][y]); 37 } 38 } 39 if(y < n && !vis[x][y + 1]){ 40 if((tem = Dfs(x,y + 1,cnt,2)) != -INF){ 41 res = max(res,tem + g[x][y]); 42 } 43 } 44 if(x > 1 && !vis[x - 1][y]){ 45 if((tem = Dfs(x - 1,y,cnt,1)) != -INF){ 46 res = max(res,tem + g[x][y]); 47 } 48 } 49 vis[x][y] = 0; 50 //printf("dp[%d][%d][%d] : %d\n",x,y,cnt,res); 51 return res; 52 } 53 54 int main(){ 55 //freopen("in.txt","r",stdin); 56 while(scanf("%d%d",&n,&k) == 2 && (n || k)){ 57 for(int i = 1; i <= n; ++i){ 58 for(int j = 1; j <= n; ++j){ 59 scanf("%d",&g[i][j]); 60 } 61 } 62 memset(vis,0,sizeof(vis)); 63 memset(caled,0,sizeof(caled)); 64 ll ans = Dfs(n,n,0,1); 65 printf("Case %d: ",++Case); 66 if(ans == -INF){ 67 printf("impossible\n"); 68 } 69 else{ 70 printf("%lld\n",ans); 71 } 72 } 73 return 0; 74 }