1
+ # Time: O(m * n)
2
+ # Space: O(m + n)
3
+ #
4
+ # The demons had captured the princess (P) and imprisoned her
5
+ # in the bottom-right corner of a dungeon. T
6
+ # he dungeon consists of M x N rooms laid out in a 2D grid.
7
+ # Our valiant knight (K) was initially positioned in the top-left room
8
+ # and must fight his way through the dungeon to rescue the princess.
9
+ #
10
+ # The knight has an initial health point represented by a positive integer.
11
+ # If at any point his health point drops to 0 or below, he dies immediately.
12
+ #
13
+ # Some of the rooms are guarded by demons,
14
+ # so the knight loses health (negative integers) upon entering these rooms;
15
+ # other rooms are either empty (0's) or contain magic orbs that increase the knight's health (positive integers).
16
+ #
17
+ # In order to reach the princess as quickly as possible,
18
+ # the knight decides to move only rightward or downward in each step.
19
+ #
20
+ #
21
+ # Write a function to determine the knight's minimum initial health
22
+ # so that he is able to rescue the princess.
23
+ #
24
+ # For example, given the dungeon below, the initial health of
25
+ # the knight must be at least 7 if he follows the optimal path RIGHT-> RIGHT -> DOWN -> DOWN.
26
+ #
27
+ # Notes:
28
+ #
29
+ # The knight's health has no upper bound.
30
+ # Any room can contain threats or power-ups, even the first room the knight enters
31
+ # and the bottom-right room where the princess is imprisoned.
32
+ #
33
+
34
+ class Solution :
35
+ # @param dungeon, a list of lists of integers
36
+ # @return a integer
37
+ def calculateMinimumHP (self , dungeon ):
38
+ DP = [float ("inf" ) for _ in dungeon [0 ]]
39
+ DP [- 1 ] = 1
40
+
41
+ for i in reversed (xrange (len (dungeon ))):
42
+ DP [- 1 ] = max (DP [- 1 ] - dungeon [i ][- 1 ], 1 )
43
+ for j in reversed (xrange (len (dungeon [i ]) - 1 )):
44
+ min_HP_on_exit = min (DP [j ], DP [j + 1 ])
45
+ DP [j ] = max (min_HP_on_exit - dungeon [i ][j ], 1 )
46
+
47
+ return DP [0 ]
48
+
49
+ # Time: O(m * n logk), where k is the possible maximum sum of loses
50
+ # Space: O(m + n)
51
+ class Solution2 :
52
+ # @param dungeon, a list of lists of integers
53
+ # @return a integer
54
+ def calculateMinimumHP (self , dungeon ):
55
+ maximum_loses = 0
56
+ for rooms in dungeon :
57
+ for room in rooms :
58
+ if room < 0 :
59
+ maximum_loses += abs (room )
60
+
61
+ return self .binarySearch (dungeon , maximum_loses )
62
+
63
+ def binarySearch (self , dungeon , maximum_loses ):
64
+ start , end = 1 , maximum_loses + 1
65
+ result = 0
66
+ while start < end :
67
+ mid = start + (end - start ) / 2
68
+ if self .DP (dungeon , mid ):
69
+ end = mid
70
+ else :
71
+ start = mid + 1
72
+ return start
73
+
74
+ def DP (self , dungeon , HP ):
75
+ remain_HP = [0 for _ in dungeon [0 ]]
76
+ remain_HP [0 ] = HP + dungeon [0 ][0 ]
77
+ for j in xrange (1 , len (remain_HP )):
78
+ if remain_HP [j - 1 ] > 0 :
79
+ remain_HP [j ] = max (remain_HP [j - 1 ] + dungeon [0 ][j ], 0 )
80
+
81
+ for i in xrange (1 , len (dungeon )):
82
+ if remain_HP [0 ] > 0 :
83
+ remain_HP [0 ] = max (remain_HP [0 ] + dungeon [i ][0 ], 0 )
84
+ else :
85
+ remain_HP [0 ] = 0
86
+
87
+ for j in xrange (1 , len (remain_HP )):
88
+ remain = 0
89
+ if remain_HP [j - 1 ] > 0 :
90
+ remain = max (remain_HP [j - 1 ] + dungeon [i ][j ], remain )
91
+ if remain_HP [j ] > 0 :
92
+ remain = max (remain_HP [j ] + dungeon [i ][j ], remain )
93
+ remain_HP [j ] = remain
94
+
95
+ return remain_HP [- 1 ] > 0
96
+
97
+ if __name__ == "__main__" :
98
+ dungeon = [[ - 2 , - 3 , 3 ], \
99
+ [ - 5 , - 10 , 1 ], \
100
+ [ 10 , 30 , - 5 ]]
101
+ print Solution ().calculateMinimumHP (dungeon )
102
+
103
+ dungeon = [[ - 200 ]]
104
+ print Solution ().calculateMinimumHP (dungeon )
105
+
106
+ dungeon = [[0 , - 3 ]]
107
+ print Solution ().calculateMinimumHP (dungeon )
0 commit comments