【牛客网动态规划专项】DP12 龙与地下城游戏问题

题目描述

【牛客网动态规划专项】DP12 龙与地下城游戏问题_第1张图片

解题思路

备忘录

dp是一个与map相同大小的矩阵:

  • dp[i][j]:从map[i][j]出发走到map[n-1][m-1](终点)所需的最少初始血量
  • 原问题解:dp[0][0]

状态转移方程

考虑从map[i][j]出发走到map[n-1][m-1]所需的最少初始血量dp[i][j],需要满足以下两个条件:

  1. 从map[i][j]出发前,也就是一步也没走的时候,要保证血量大于等于1: d p [ i ] [ j ] ≥ 1 dp[i][j] \geq 1 dp[i][j]1
  2. 走了一步之后,也就是已经走到 m a p [ i ] [ j ] map[i][j] map[i][j]位置、正准备接着走到下一个位置 m a p [ i n e x t ] [ j n e x t ] map[i_{next}][j_{next}] map[inext][jnext]的时候,要保证当前剩余血量大于等于从下一个位置出发走到终点所需的最少初始血量:
    d p [ i ] [ j ] + m a p [ i ] [ j ] ≥ d p [ i n e x t ] [ j n e x t ] d p [ i ] [ j ] ≥ d p [ i n e x t ] [ j n e x t ] − m a p [ i ] [ j ] \begin{aligned} dp[i][j]+map[i][j] &\geq dp[i_{next}][j_{next}] \\ dp[i][j] &\geq dp[i_{next}][j_{next}] - map[i][j] \end{aligned} dp[i][j]+map[i][j]dp[i][j]dp[inext][jnext]dp[inext][jnext]map[i][j]

将这两个条件合并起来就是:
d p [ i ] [ j ] = m a x { 1 d p [ i n e x t ] [ j n e x t ] − m a p [ i ] [ j ] } (1) dp[i][j] = max\left\{\begin{matrix} \begin{aligned} & 1\\ & dp[i_{next}][j_{next}] - map[i][j] \end{aligned} \end{matrix}\right\} \tag{1} dp[i][j]=max{1dp[inext][jnext]map[i][j]}(1)
如果下一个位置 m a p [ i n e x t ] [ j n e x t ] map[i_{next}][j_{next}] map[inext][jnext]有多种选择,可以分别对每一种选择计算需要的最少初始血量(公式1),然后选择最小的一个:
d p [ i ] [ j ] = m i n { m a x { 1 , d p [ i n e x t 1 ] [ j n e x t 1 ] − m a p [ i ] [ j ] } m a x { 1 , d p [ i n e x t 2 ] [ j n e x t 2 ] − m a p [ i ] [ j ] } . . . } (2) dp[i][j] = min \left\{ \begin{matrix} max\{1, dp[i_{next1}][j_{next1}]-map[i][j]\} \\ max\{1, dp[i_{next2}][j_{next2}]-map[i][j]\} \\ ... \end{matrix} \right\} \tag{2} dp[i][j]=min max{1,dp[inext1][jnext1]map[i][j]}max{1,dp[inext2][jnext2]map[i][j]}... (2)

根据上面的公式得出下面的状态转移方程:

  1. 最后一个位置( i = n − 1 , j = m − 1 i=n-1, j=m-1 i=n1,j=m1
    dp[n-1][m-1] = max(1, 1-map[n-1][m-1])
    
    因为没有下一个位置了,所以上面的第二个条件退化为“保证当前剩余血量大于等于1”,即
    d p [ n − 1 ] [ m − 1 ] + m a p [ n − 1 ] [ m − 1 ] ≥ 1 d p [ n − 1 ] [ m − 1 ] ≥ 1 − m a p [ n − 1 ] [ m − 1 ] \begin{aligned} dp[n-1][m-1]+map[n-1][m-1] &\geq 1 \\ dp[n-1][m-1] &\geq 1 - map[n-1][m-1] \end{aligned} dp[n1][m1]+map[n1][m1]dp[n1][m1]11map[n1][m1]
  2. 最后一列( j = m − 1 j=m-1 j=m1
    for i from n-2 to 0: 
    	dp[i][m-1] = max(1, dp[i+1][m-1] - map[i][m-1])
    
    map最后一列的每一个位置都只能向下走,即 d p [ i n e x t ] [ j n e x t ] = d p [ i + 1 ] [ m − 1 ] dp[i_{next}][j_{next}] = dp[i+1][m-1] dp[inext][jnext]=dp[i+1][m1]
  3. 最后一行( i = n − 1 i=n-1 i=n1
    for j from m-2 to 0:
    	dp[n-1][j] = max(1, dp[n-1][j+1] - map[n-1][j]),
    
    map最后一行的每一个位置都只能向右走,即 d p [ i n e x t ] [ j n e x t ] = d p [ n − 1 ] [ j + 1 ] dp[i_{next}][j_{next}] = dp[n-1][j+1] dp[inext][jnext]=dp[n1][j+1]
  4. 一般情况( 0 ≤ i ≤ n − 2 , 0 ≤ j ≤ m − 2 0\leq i \leq n-2, 0\leq j \leq m-2 0in2,0jm2
    for i from n-2 to 0:
    	for j from m-2 to 0:
    		down = max(1, dp[i+1][j]-map[i][j]);
    	    right = max(1, dp[i][j+1]-map[i][j]);
    	    dp[i][j] = min(down, right);
    
    下一个位置有两种选择:
    • 向下走,即 d p [ i n e x t 1 ] [ j n e x t 1 ] = d p [ i + 1 ] [ j ] dp[i_{next1}][j_{next1}] = dp[i+1][j] dp[inext1][jnext1]=dp[i+1][j]
    • 向右走,即 d p [ i n e x t 2 ] [ j n e x t 2 ] = d p [ i ] [ j + 1 ] dp[i_{next2}][j_{next2}] = dp[i][j+1] dp[inext2][jnext2]=dp[i][j+1]

C++代码实现

#include 
#include 
using namespace std;

int n, m; 
int** map; // map: n*m
void input(){
   cin >> n >> m;

   map = new int*[n];
   for(int i=0; i<n; i++){
       map[i] = new int[m];
   }

   for(int i=0; i<n; i++){
       for(int j=0; j<m; j++){
           cin >> map[i][j];
       }
   }
}

int** dp; // 与map相同大小,dp: n*m
int solve(){
   dp = new int*[n];
   for(int i=0; i<n; i++){
       dp[i] = new int[m];
   }

   // dp[i][j]: 从map[i][j]位置走到map[n-1][m-1]位置所需的最少初始血量

   // init
   dp[n-1][m-1] = max(1, 1-map[n-1][m-1]);
    
   for(int i=n-2; i>=0; i--){
       dp[i][m-1] = max(1, dp[i+1][m-1] - map[i][m-1]);
   }

   for(int j=m-2; j>=0; j--){
       dp[n-1][j] = max(1, dp[n-1][j+1] - map[n-1][j]);
   }

   // bottom-up clac
   for(int i=n-2; i>=0; i--){
       for(int j=m-2; j>=0; j--){
           int down = max(1, dp[i+1][j]-map[i][j]);
           int right = max(1, dp[i][j+1]-map[i][j]);
           dp[i][j] = min(down, right);
       }
   }

   return dp[0][0];
}

int main() {
   input();
   cout << solve();
}

你可能感兴趣的:(算法学习笔记,动态规划,游戏,算法)