[Daily Easy Question] Hard difficulty dynamic programming question on Leetcode - Implementation of dungeon game

Insert image description here

Junxi_'s personal homepage

No matter how far you go, never forget your original intention when you started the journey

C/C++ game development

Hello, Minasans, this is Junxi_, a blogger who has been studying dynamic programming algorithms recently. I recently encountered a Hard difficulty dynamic programming question when I was solving questions on Leetcode. Today I will take this opportunity to share it with you. Here are some of my opinions and solutions to this topic (don’t worry, I’m an AC)

  • Okay, without further ado, let’s start our study today! !

dungeon games

Insert image description here

Insert image description here

  • Okay, okay, I can't understand what it means when I look at a lot of words in the title. Then I look at the difficulty level marked above. A lot of people believe that just like the blogger, they are ready to click out first. Don't be impatient yet. Let me take you step by step to analyze the meaning of this question

Question analysis

Insert image description here
(ps: This one is really a princess in the comics)

  • Our princess was captured and locked up in the lower right corner, as shown in the picture
    Insert image description here
  • Next, our knight has to start from the position in the picture. If he encounters demons (that is, the value in the grid is negative), he needs to fight them and his blood will be deducted. When he encounters the magic ball (positive value in the picture), It can restore blood. At this time, the question asks us how much blood the knight needs at least when in the initial position (it is stipulated that when the blood volume at a certain position is greater than or equal to 1, you can pass, otherwise you will fail)
  • So, through the description of the topic and combined with the dynamic programming ideas we learned before, do you find anything different? As a Hard difficulty question, it is definitely impossible to solve it using conventional thinking. Well, let me take you through a detailed analysis of the algorithm principles.

Algorithm principle

1. Status display

  • We have said before in the dynamic programming algorithm that when encountering dynamic programming problems, the general solution is to divide into two situations:
    • (1) Select a certain position as the end point, establish a dp table, and display the status
    • (2) Select a certain location as the starting point...
  • According to the conventional thinking, since we know the location of the princess, the normal situation is to choose the first situation to try to represent the status.
  • If we follow this idea and define this question as: starting from the starting point and reaching the position [i, j], the minimum number of initial health points required.
  • Then there will be a problem when we analyze the state transfer: that is, our current health points will also be affected by the subsequent paths. That is to say, state transfer from top to bottom cannot solve the problem well.

Why? Let's imagine that our knight's health is very low at this time. Whether facing down or to the right in the next square, we will encounter a demon and our knight's health will be deducted as a negative number. Is the dp value here reasonable at this time? It's obviously unreasonable. Therefore, instead of considering the situation in front of us, we also need to consider the situation of the path behind. Isn't it too troublesome?

  • At this time, we need to change the state to represent: starting from the position [i, j], the minimum initial health points required to reach the end point. In this way, when we analyze the state transition, we do not need to consider the previous path, and the subsequent optimal state is already known, which greatly simplifies the difficulty of our analysis.

  • In summary, the defined state is expressed as:
    dp[i][j] means: starting from the position [i, j], the minimum initial required to reach the end point Health Points


2 State transition equation

  • For dp[i][j], starting from the position [i, j], there are two choices for the next step (for the convenience of understanding, let the final answer of dp[i][j] be x):

  • i. Go to the right, then toward the finish line

  • Then our lowest health points at position [i, j] plus the consumption of this position should be greater than or equal to the lowest health points at the right position, that is: x + dungeon[i][j ] >= dp[i][j + 1] .
    By shifting terms, we get: x >= dp[i][j + 1] - dungeon[i][j] . Because we want the minimum value, in this case x = dp[i][j + 1] - dungeon[i][j] ;

  • ii. Go down and then toward the end

  • Then our lowest health points at position [i, j] plus the consumption of this position should be greater than or equal to the lowest health points at the lower position, that is: x + dungeon[i][j ] >= dp[i + 1][j] .
    By shifting terms, we get: x >= dp[i + 1][j] - dungeon[i][j] . Because we want the minimum value, in this case x = dp[i + 1][j] - dungeon[i][j] ;

  • To sum up, what we need is the minimum value in the two situations, so the available state transition equation is:
    dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j]

  • However, if the dungeon[i][j] at the current position is a relatively large positive number, the value of dp[i][j] may become 0 or a negative number. That is, the lowest point will be less than 1, then the rider will die. Therefore, if the dp[i][j] we find is less than or equal to 0, it means that the lowest initial value at this time should be 1. To handle this situation, you only need to let dp[i][j] and 1 take a maximum value:
    dp[i][j] = max(1, dp[i][j])

What does that mean? It is [i,j] here that will restore a lot of blood, but if the dp[i,j] at this time is a negative number, it means that the minimum blood volume required for the knight here is 0 or a negative number. This is obviously does not meet the requirements, so we need to handle this special situation as mentioned above

initialization

  • You can add an "auxiliary node" at the front to help us initialize. There are two points to note when using this technique:
  • i. The value in the auxiliary node must "ensure that the subsequent form filling is correct";
  • ii. "Mapping relationship of subscripts".

The usage of auxiliary nodes has been discussed in the blog linked above, so I won’t go into details here.

  • In this question, since we need to consider the impact of the subsequent path on the current position, we need to add a row at the end of the dp table, and after adding a column, all values ​​are first initialized to infinity, and then dp[m][n - 1] 或dp[m - 1][n] = 1 is enough.

Order of filling in the form

  • According to the "state transition equation", we need to "fill in each row from bottom to top" and "fill in each row from right to left." After reading the above algorithm analysis, this should not be difficult to understand.

return value

  • As can be seen from the title, our knight starts from the upper left corner, so combined with the above analysis, the value we need to return is dp[0][0]

Write code

class Solution {
    
    
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) {
    
    
        int m=dungeon.size();
        int n=dungeon[0].size();
        //建立dp表,以某个位置为开始建立状态转移方程
        vector<vector<int>> dp(m+1,vector<int>(n+1,INT_MAX));
        dp[m][n-1]=1;//考虑边界问题
        for(int i=m-1;i>=0;i--)
        {
    
    
            for(int j=n-1;j>=0;j--)
            {
    
    
            	//填表
                dp[i][j]=min(dp[i+1][j],dp[i][j+1])-dungeon[i][j];
                dp[i][j]=max(1,dp[i][j]);
            }
        }
        //返回值
        return dp[0][0];

    }
};
  • The code is very simple, with only a dozen lines. What is actually difficult is the process of analyzing the problem above and the judgment of some special situations. I believe that if you can understand the analysis of the above principles, this should not be a problem for you at all. Disaster.

Summarize

  • Okay, that’s it for today! In fact, the code is not important. It is important to be able to understand the hidden principles and learn the analysis of the corresponding questions through this question. Therefore, if you want to really learn it, you might as well try to understand the algorithm principles from the beginning and then write the code independently. In this way, I I believe it is the best way to improve your understanding of dynamic programming topics.
  • If you have any questions or doubts about the content of the article, please feel free to ask them in the comment area. Of course, you can also send me a private message and I will reply as soon as possible! !

It is not easy for new bloggers to create. If you feel that the content of the article is helpful to you, you might as well read it three times in a row before leaving. Your support is my motivation to update! ! !

**(Keli asks you to support the blogger three times in a row!!! Click on the comments below to like and collect to help Keli)**

Insert image description here

Guess you like

Origin blog.csdn.net/syf666250/article/details/134796487