蠡口174. Dungeon Game

一开始想的是设所求数为x,那么遍历所有可能路径,看最小血量花销。

后来想想对矩阵一顿操作后,返回一个值,而非所有路径的具体值,很有可能是DP。于是往DP的方向想,我们能确定的是救到公主后,骑士的血量至少为1,然后求起始位置是骑士的最低血量可以是多少。这样一想,自然是从右下往左上递推。

1、我们建立dp二维数组:dp[i][j]表示进入房间(i,j)之前骑士血量可以最低是多少。

2、初始边界:

  2.1)我们先来想想进入最后的房间之前骑士血量可以最低是多少?最后一个房间可以是:

    1)怪兽(即dungeon[m-1][n-1]<0),那么骑士在这个房间会失血,如果想活着求出公主,那么进入最后的房间之前骑士至少得有1-dungeon[m-1][n-1]的血量。故dp[m-1][n-1]=1-dungeon[m-1][n-1];

    2)空房间(即dungeon[m-1][n-1]==0),那骑士在进入最后的房间之前只需有1滴血就可以继续苟延残喘地就下公主。故dp[m-1][n-1]=1;

    3)魔法加成(即dungeon[m-1][n-1]>0),那骑士在进入最后的房间之前,也需1滴血,不然骑士在开最后一个房门的时候就死了。dp[m-1][n-1]=1;

综合1)、2)、3)不难总结出dp[m-1][n-1]=max(1,1-dungeon[m-1][n-1]) 。

  2.2)最右边那一列只能从上往下走,也就是只能从下往上递推,注意从(i,n-1)走出来之后的血量就是进入(i+1,n-1)之前的血量。不管房间(i,n-1)里是恶魔、空房间、还是魔法加成,dp[i][n-1]+dungeon[m-1][n-1]=dp[i+1][n-1] => dp[i][n-1]=dp[i+1][n-1]-dungeon[m-1][n-1],同时不要忘了进入房间之前血量至少为1。所以dp[i][n-1]=max(1,dp[i+1][n-1]-dungeon[m-1][n-1])

  2.3)最下面那一行只能从左往右走,也就是只能从右向左递推,同2.2)dp[m-1][j]=max(1,dp[m-1][j+1]-dungeon[m-1][j])

3、建立递推关系:

  从(i,j)房间出发营救公主只有两条路线:向右和向下。走出(i,j)房间后的血量至少为min(d[i+1][j],d[i][j+1]),由于房间(i,j)会损失-dungeon[i][j]滴血,那么走进(i,j)前的血量至少要是min(d[i+1][j],d[i][j+1])-dungeon[i][j],这个值可能小于1,所以dp[i][j]=max(1,min(dp[i+1][j],dp[i][j+1])-dungeon[i][j])

4、返回值:应该返回dp[0][0]

class Solution(object):
    def calculateMinimumHP(self, dungeon):
        """
        :type dungeon: List[List[int]]
        :rtype: int
        """
        m,n=len(dungeon),len(dungeon[0])
        #dp[i][j]: knight's minimal health at upon reach (i,j) so that he is able to rescue the princess
        dp=[[0 for j in range(n)] for i in range(m)]
        dp[m-1][n-1]=max(1,1-dungeon[m-1][n-1])
        for i in range(m-2,-1,-1): dp[i][n-1]=max(1,dp[i+1][n-1]-dungeon[i][n-1])
        for j in range(n-2,-1,-1): dp[m-1][j]=max(1,dp[m-1][j+1]-dungeon[m-1][j])
        for i in range(m-2,-1,-1):
            for j in range(n-2,-1,-1):
                dp[i][j]=max(1,min(dp[i+1][j],dp[i][j+1])-dungeon[i][j])
        print(dp)
        return(dp[0][0])

猜你喜欢

转载自www.cnblogs.com/Leisgo/p/11742836.html