本题选自计蒜客动态规划习题
【题目描述】
蒜头君在玩一款逃生的游戏。在一个n×m的矩形地图上,蒜头位于其中一个点。地图上每个格子有加血的药剂,和掉血的火焰,药剂的药效不同,火焰的大小也不同,每个格子上有一个数字,如果格子上的数字是正数说明是一个药剂代表增加的生命值,如果是负数说明是火焰代表失去的生命值。 蒜头初始化有v点血量,他的血量上限是c,任何时刻他的生命值都不能大于血量上限,如果血量为0则会死亡,不能继续游戏。
矩形地图上的四个角(1,1),(1,m),(n,1),(n,m)为游戏的出口。游戏中只要选定了一个出口,就必须朝着这个方向走。例如,选择了左下的出口,就只能往左和下两个方向前进,选择了右上的出口,就只能往右和上两个方向前进,左上和右下方向的出口同理。 如果成功逃生,那么剩余生命值越高,则游戏分数越高。为了能拿到最高分,请你帮忙计算如果成功逃生最多能剩余多少血量,如果不能逃生输出-1。
【输入格式】
第一行依次输入整数n,m,x,y,v,c(1<n,m≤1000,1≤x≤n,1≤y≤m,1≤v≤c≤10000),其中n,m代表地图大小,(x,y)代表蒜头君的初始位置,v代表蒜头的初始化血量,c代表蒜头的生命值上限。 接下来n行,每行有m个数字,代表地图信息。(每个数字的绝对值不大于100, 地图中蒜头君的初始位置的值一定为0)
【输出格式】 一行输出一个数字,代表成功逃生最多剩余的血量,如果失败输出-1。
【样例输入】
4 4 3 2 5 10
1 2 3 4
-1 -2 -3 -4
4 0 2 1
-4 -3 -2 -1
【样例输出】
10
【分析】(个人观点,仅供参考)
此题特意设置4个出口,相当于把4个方向的迷宫问题组合在一起。
因此,可以考虑分别进行四个方向的动态规划,将到达4个出口的最大值输出。
因为不想写四段程序,所以简化了记忆化搜索(即便如此,还有8个方向需要判断),根据(i, j) 与起点 (x, y) 的相对位置,判断当前走向,调用状态转移方程。 和 (x, y) 同行同列的点,只能由单一方向过来,故需要单独考虑。
搜索过程如果遇到血量为 0 的情况,可以将状态值设置为极小值,避免影响结果;
如果超过血量上限,则更新血量为上限值 c。
ans 初始化为 -1,如果没有血量更高的出口,则保持 -1 输出。
#include<iostream>
using namespace std;
int a[1001][1001], dp[1002][1002], ans= -1,n,m,x,y,v,c;
int dfs(int i,int j){ //返回 到 a[i][j] 的最大血量
if(dp[i][j]) return dp[i][j]; //该点已经计算过
if(i<x && j==y) dp[i][j] = dfs(i+1,j) +a[i][j]; //计算同行同列
else if(i>x && j==y) dp[i][j] = dfs(i-1,j) +a[i][j];
else if(i==x && j<y) dp[i][j] = dfs(i,j+1) +a[i][j];
else if(i==x && j>y) dp[i][j] = dfs(i,j-1) +a[i][j];
else if(i>x && j>y) dp[i][j] = max( dfs(i-1,j), dfs(i,j-1) ) + a[i][j]; //出口在右下方
else if(i>x && j<y) dp[i][j] = max( dfs(i-1,j), dfs(i,j+1) ) + a[i][j]; //出口在左下方
else if(i<x && j>y) dp[i][j] = max( dfs(i+1,j), dfs(i,j-1) ) + a[i][j]; //出口在右上方
else if(i<x && j<y) dp[i][j] = max( dfs(i+1,j), dfs(i,j+1) ) + a[i][j]; //出口在左上方
if(dp[i][j]<=0) dp[i][j]= -2000000000; //已死亡
if(dp[i][j]>c) dp[i][j]= c; //超血量
return dp[i][j];
}
int main(){
cin>>n>>m>>x>>y>>v>>c;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
dp[x][y] = a[x][y]+v; //记录起点的初始体力 ,放入状态数组
ans = max( ans, dfs(1,1) ) ;
ans = max( ans,dfs(1,m) ) ;
ans = max( ans, dfs(n,1) ) ;
ans = max( ans,dfs(n,m) ) ;
cout<<ans;
return 0;
}