DFS + Memorization
DFS + Memorization + Pruning
If there exist one way that the other one will NOT win, then I win!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution : def canIWin (self, maxChoosableInteger, desiredTotal) : if maxChoosableInteger * (maxChoosableInteger + 1 ) / 2 < desiredTotal: return False self.memo = {} return self.helper(list(range(1 , maxChoosableInteger + 1 )), desiredTotal) def helper (self, nums, desiredTotal) : key = tuple(nums) if key in self.memo: return self.memo[key] if nums[-1 ] >= desiredTotal: return True for i in range(len(nums)): if not self.helper(nums[:i]+nums[i+1 :], desiredTotal-nums[i]): self.memo[key] = True return True self.memo[key] = False return False
1 2 3 4 5 6 7 class Solution : def generatePossibleNextMoves (self, s) : res = [] for i in range(len(s)-1 ): if s[i] == '+' and s[i+1 ] == '+' : res.append(s[:i] + "-" + "-" + s[i+2 :]) return res
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution : def canWin (self, s) : self.memo = {} return self.win(s) def win (self, s) : if s in self.memo: return self.memo[s] for i in range(len(s)-1 ): if s[i] == '+' and s[i+1 ] == '+' : news = s[:i] + "-" + "-" + s[i+2 :] if not self.win(news): self.memo[s] = True return True self.memo[s] = False return False
DP Solution1. DFS + Memorization
TLE1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution : def firstWillWin (self, n) : self.memo = {} return self.helper(n) def helper (self, n) : if n <= 2 : return True if n in self.memo: return self.memo[n] if not self.firstWillWin(n-1 ) or not self.firstWillWin(n-2 ): self.memo[n] = True return True self.memo[n] = False return False
Solution2. DP
Time : O(n), Space : O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution : def firstWillWin (self, n) : if n == 0 : return False if n <= 2 : return True dp = [False ] * (n+1 ) dp[1 ] = dp[2 ] = True for i in range(3 , n+1 ): dp[i] = not dp[i-1 ] or not dp[i-2 ] return dp[n]
Solution3. Math 1 2 3 class Solution : def firstWillWin (self, n) : return bool(n % 3 )
dp[i], preSum[i]-dp[i] represent how many i get and how many other one get!!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution : def firstWillWin (self, values) : n = len(values) if n <= 2 : return True values.reverse() psum = self.preSum(values) dp = [0 ] * n dp[0 ] = psum[0 ] dp[1 ] = psum[1 ] for i in range(2 , n): dp[i] = max(psum[i] - dp[i-1 ], psum[i] - dp[i-2 ]) return dp[n-1 ] > psum[n-1 ] - dp[n-1 ] def preSum (self, values) : psum = [values[0 ]] for i in range(1 , len(values)): psum.append(psum[-1 ] + values[i]) return psum
TODO!!!
Follow up DP[i][j] represents from i-th coin to the j-th coin, the largest sum of pizza that i can pick.
case1 : if i take the left coin,
if input[i+1] < input[j], input[i] + DP[i+1][j-1]
if input[i+1] > input[j], input[i] + DP[i+2][j]
case2 : if i take the right coin,
if input[i] < input[j-1], input[j] + DP[i][j-2]
if input[i] > input[j-1], input[j] + DP[i+1][j-1]
2 coins, DP[i][i+1] = max(input[i], input[i+1])
1 coin, DP[i][i] = input[i]
Let us understand the problem with few examples:
5, 3, 7, 10 : The user collects maximum value as 15(10 + 5)
8, 15, 3, 7 : The user collects maximum value as 22(7 + 15) Does choosing the best at each move give an optimal solution?
F(i, j) represents the maximum value the user can collect from i’th coin to j’th coin.
If I take Ai, Vi + min(F(i+2, j), F(i+1, j-1)
If I take Ak, Vj + min(F(i+1, j-1), F(i, j-2) $$F(i, j) = Max(Vi + min(F(i+2, j), F(i+1, j-1) ), Vj + min(F(i+1, j-1), F(i, j-2) ))$$
F(i, j) = Vi If j == i
F(i, j) = max(Vi, Vj) If j == i+1