LeetCode刷题碎碎念(二):DP (1)

DP

高频

5. Longest Palindromic Substring (Medium)

Backtracking

class Solution:
    def longestPalindrome(self, s: str) -> str:
        self.maxLen = 0
        self.left = 0
        
        if not s or len(s) == 1: return s
        
        def helper(s, l, r):
            # 没有超出s边界 and 左右两端相等(回文)
            while l >= 0 and r < len(s) and s[l] == s[r]:
                # 新的长度更大,就更新substring位置
                if (r - l + 1) > self.maxLen:
                    self.left = l
                    self.maxLen = r - l + 1
                # 指针向左右两边移动
                l -= 1
                r += 1
                
        for i in range(len(s)):
            helper(s, i, i)     # 用于"aba"型回文
            helper(s, i, i + 1) # 用于"abba"型回文
        
        return s[self.left: self.left + self.maxLen]

I: O(n), S = O(n), T = O(n)

70. Climbing Stairs (Easy)

You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

Dynamic Programming 动态规划:n步的解=n-1步的解(再爬一格)+n-2步的解(再爬两个)。Fibonacci数列。

class Solution:
    def climbStairs(self, n: int) -> int:
        f = []  # f[i] 爬完i节楼梯的解
        f.append(1) #f[0] = 1, 爬完0节楼梯:不爬
        f.append(1) #f[1] = 1, 第一节楼梯只有一个解,爬一格
        for i in range(2,n+1):
            f.append(f[i-1] + f[i-2])   #f[i] = f[i-1]+f[i-2]
        
        return f[n]
746. Min Cost Climbing Stairs (Easy)

On a staircase, the i-th step has some non-negative cost cost[i] assigned (0 indexed).
Once you pay the cost, you can either climb one or two steps. You need to find minimum cost to reach the top of the floor, and you can either start from the step with index 0, or the step with index 1.

Note:
cost will have a length in the range [2, 1000].
Every cost[i] will be an integer in the range [0, 999].

在这里插入图片描述

  • 误【arr(n)应为dp(n)】
  • 方法二:dp(n)已经包含n th节的过路费。最后top没有cost,需要取之前两节的代价最小值。
    pre, cur = cost[0], cost[1]
    for i in range(2, len(cost)):
        pre, cur = cur, min(pre, cur) + cost[i]
    return min(pre,cur)

Python中,如果要给空list赋值(而非append),list需要初始化,否则会报出超出list范围的错误:

  • 一维list初始化递增:L = range(10)
  • 一维list初始化常数:L = [0] * 5
  • 二维list:
L = [[0] * 5 for i in range(3)]
#print(L)
#[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]

Method 1: Recursion + Memorization (min cost to climb nth

def minCostClimbingStairs(self, cost: List[int]) -> int:
    # Recursion + Memorization
        memo = [0] * (len(cost)+1)
        
        def dp(cost, memo, i):
            if i <= 1: return 0 # 0 or 1 stair, no need to pay
            if memo[i] > 0: return memo[i]
                # 有cost,已经求解过了-> 终止条件
            memo[i] = min(dp(cost, memo, i-1) + cost[i-1], dp(cost, memo, i-2) + cost[i-2])
            return memo[i]
        
        return dp(cost, memo, len(cost))

记忆递归自上而下,从top开始向下寻找。

Method 2: DP (min cost to climb nth

class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:
    # DP
        dp = [0] * (len(cost)+1)
        for i in range(2, len(cost)+1):
            dp [i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2])

        return dp[-1]

T: O(n), S: O(n)

class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:
        dp1 = dp2 = 0
        
        for x in reversed(cost):
            tmp = x + min(dp1, dp2)
            dp2 = dp1
            dp1 = tmp
        
        return min(dp1, dp2)

T: O(n), S: O(1)

DP从起点开始向top寻找。

1137. N-th Tribonacci Number (Easy)

The Tribonacci sequence Tn is defined as follows:
T0 = 0, T1 = 1, T2 = 1, and Tn+3 = Tn + Tn+1 + Tn+2 for n >= 0.
Given n, return the value of Tn.
Example 1:
Input: n = 4
Output: 4
Explanation:
T_3 = 0 + 1 + 1 = 2
T_4 = 1 + 1 + 2 = 4

class Solution:
    def tribonacci(self, n: int) -> int:
        if n == 0: return 0
        if n == 1 or n == 2: return 1
        t = [0] * (n+1)
        t[0] = 0
        t[1] = t[2] = 1
        for i in range(3, n+1):
            t[i] = t[i-1] + t[i-2] + t[i-3]
        
        return t[n]
303. Range Sum Query - Immutable (Easy)

Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive.
Example:
Given nums = [-2, 0, 3, -5, 2, -1]
sumRange(0, 2) -> 1
sumRange(2, 5) -> -1
sumRange(0, 5) -> -3

Solution 1: 什么处理都不做,直接brute force:

sum = 0
for k in range(i, j+1):
	sum += nums[k]
return nums

时间复杂度O(m * n), 空间复杂度O(n). 1500 ms
Solution 2: 结果存在二维数组内

n = len(nums)
        if n == 0: return
        sums_ = [0] * n
        for i in range(0, n):
            for j in range(i+1, n):
               for  p in range(i, j):
                sums_[i][j] += nums[p]

sums_[i][j]: i~j之间的sum。
时间复杂度:O(n^3), 空间复杂度:O(n^2) 内存不够
Solution 3: 用一维储存sum
求2~5的和,5的和减去2的合。
在这里插入图片描述

    def __init__(self, nums: List[int]):
        n = len(nums)
        if n == 0: return
   	# 构建sums_
        # sums_[i]: nums[0]+nums[1]+...+nums[i] 前i个元素的和
        # sums_[i+1] = sums_[i]+nums[i+1]
        self.sums_ = [0] * (n+1)
        self.sums_[0] = nums[0]
        for i in range(1, n):
            self.sums_[i] = self.sums_[i-1] + nums[i]
            
    def sumRange(self, i: int, j: int) -> int:
        if i == 0: return self.sums_[j] # 从0开始,不需要减去前面的和
        return self.sums_[j] - self.sums_[i-1]

时间复杂度:O(n) + m*O(1) = O(m + n), 空间复杂度:O(n)

1218. Longest Arithmetic Subsequence of Given Difference (Medium)

Given an integer array arr and an integer difference, return the length of the longest subsequence in arr which is an arithmetic sequence such that the difference between adjacent elements in the subsequence equals difference.
Example 1:
Input: arr = [1,2,3,4], difference = 1
Output: 4
Explanation: The longest arithmetic subsequence is [1,2,3,4].
Example 2:
Input: arr = [1,3,5,7], difference = 1
Output: 1
Explanation: The longest arithmetic subsequence is any single element.

数据规模太大,brute force时间复杂度 O(n^2), 超时。
DP:
dp[x] = length of longest arithmetic subsequence ends with x
[…, x-2d, x-d, x]
dp[x] = dp[x-d] + 1, if x-d exists
= 1m , if x-d doesn’t exist
Ans: max(dp)
Time: O(n), space: O(n)

Example 3:
Input: arr = [1,5,7,8,5,3,4,2,1], difference = -2
Output: 4
Explanation: The longest arithmetic subsequence is [7,5,3,1].
在这里插入图片描述
x: 当前元素
dp: {元素值:长度}
1-(-1),table中没有,放进table;下一个是5, 5-(-2)= 7不存在,放进table。。。;7;8
5-(-2)=7,7已经存在在table中,所以可以扩展{7} (7:1)变成{7,5} (5:2)。。。
最后max。
*dict赋值:m = {}
m[3] = 1
print(m): {3: 1}

def longestSubsequence(self, arr: List[int], difference: int) -> int:
        dp = {}
        for x in arr:
            dp[x] = dp[x - difference] + 1 if (x - difference) in dp else 1
        return max(dp.values())
53. Maximum Subarray (Easy)

Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
Example:
Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        n = len(nums)
        max_sum = nums[0]
        # nums[i]: maxSubarray(0,...i)
        for i in range(1, n):
            if nums[i-1] > 0:   # 不加负数!
                nums[i] += nums[i-1]
            max_sum = max(nums[i], max_sum)
        
        return max_sum

新的nums[i]代表前i个数最大的和。如果发现前面的最大和是负数,那么就不加,保证区域最大。

121. Best Time to Buy and Sell Stock (Easy)

Say you have an array for which the ith element is the price of a given stock on day i.
If you were only permitted to complete at most one transaction (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit.
Example 1:
Input: [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
Example 2:
Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
前i天以最低的价格买进,然后在第i天卖出的profit存在P[i]中。二维变成了一维。

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        L = []
        P = []
        if not len(prices): return 0
        L.append(prices[0])
        P.append(0)
        for i in range(1, len(prices)):
            L.append(min(prices[i], L[i-1]))
            P.append(max(P[i-1], prices[i] - L[i]))
            
        return P[len(prices)-1]

在这里插入图片描述

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        if n<2: return 0
        gains = [0] * (n-1)
        for i in range(1, n):
            gains[i-1] = prices[i] - prices[i-1]
    
        def maxSubArray(nums):
            P = [0] * len(nums)
            P[0] = nums[0]
            for i in range(1, len(nums)):
                P[i] = max(P[i-1] + nums[i], nums[i])
            return max(P)
        
        return max(0, maxSubArray(gains))

122. Best Time to Buy and Sell Stock II

允许多次买卖。相邻两个点有增长、相同和减少三种关系,只需要把所有相邻差值为正数(增长)的全部加起来。

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        if not prices and n == 1:
            return 0
        
        res = 0
        for i in range(1, n):
            if prices[i] > prices[i-1]: # 只有涨价
                res += prices[i] - prices[i-1]  # 才买卖
        
        return res

309. Best Time to Buy and Sell Stock with Cooldown

多次买卖,cooldown一天(卖出的第二天不能买进)

hold & unhold

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        if not prices or n <= 1:
            return 0
        
        hold = [0] * n  # 第i天手中有股票的最大利润
        unhold = [0] * n    # 第i天手中无股票的最大利润
        hold[0] = -prices[0]    # 第一天买进
        
        for i in range(1, n):
            if i == 1:
                hold[i] = max(-prices[0], -prices[1])
            else:
                # 第i天持股 = 之前就持股 + i-2天卖出,i天刚刚买进
                hold[i] = max(hold[i-1], unhold[i-2] - prices[i])
            
            # 第i天手中无股 = 前一天手里就无股 + 前一天刚刚卖出去
            unhold[i] = max(unhold[i-1], hold[i-1] + prices[i])
            
        return unhold[n-1]
class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        sold = 0
        rest = 0
        hold = -9999
        for price in prices:
            prev_sold = sold
            sold = hold + price
            hold = max(hold, rest - price)
            rest = max(rest, prev_sold)
        
        return max(rest, sold)

I: O(mn), S = O(mn), T = O(mn)

62. Unique Paths (Medium)

A robot is located at the top-left corner of a m x n grid (marked ‘Start’ in the diagram below).
The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked ‘Finish’ in the diagram below).
How many possible unique paths are there?

Example 1:
Input: m = 3, n = 2
Output: 3
Explanation:
From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:

  1. Right -> Right -> Down
  2. Right -> Down -> Right
  3. Down -> Right -> Right
    在这里插入图片描述
    一个格子被走到的方法 = 它左边的格子的方法数 + 它上边的格子的方法数
    从终点开始递归到起点。
    在这里插入图片描述
    把求过的值存在内存中:记忆化递归。
    List[m][n]: m第几行,n第几列。从0开始。
    m x n格子:m列,n行
    Solution 1:Cache Recursion
def uniquePaths(self, m: int, n: int) -> int:
        # 记忆化递归
        # 如果不把初始化list拎出来到递归外就会超时
        grid = [[0 for _ in range(n+1)] for i in range(m+1)]
        
        def findPath(m, n):
            if m < 0 or n < 0: return 0
            if m == 1 and n == 1: return 1  # at the start
            if grid[m][n] > 0: return grid[m][n]
            left_path = findPath(m-1, n)
            up_path = findPath(m, n-1)
            grid[m][n] =left_path + up_path
            return grid[m][n]
        return findPath(m, n)

新建了一个函数,不包含初始化List,用来递归寻找路径,防止超时,初始化只做一次。

Solution 2:DP

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        grid = [[1] *  m] *n    # 使得第一行和第一列全是1,可以直接跳过
                                # 因为到他们的路只有一个方向: 向下或者向左,只有一条路
        for y in range(1, n):
            for x in range(1, m):   # 跳过第一行 第一列后,其他正常
                grid[y][x] = grid[y][x-1] + grid[y-1][x]
                
        return grid[n-1][m-1]
63. Unique Paths II (Medium)

Now consider if some obstacles are added to the grids. How many unique paths would there be?
Example 1:
Input:
[ [0,0,0],
[0,1,0],
[0,0,0] ]
Output: 2
Explanation:
There is one obstacle in the middle of the 3x3 grid above.
There are two ways to reach the bottom-right corner:

1. Right -> Right -> Down -> Down
2. Down -> Down -> Right -> Right

obstacle的格子的解标记为0。

class Solution(object):
    def uniquePathsWithObstacles(self, obstacleGrid):
        """
        :type obstacleGrid: List[List[int]]
        :rtype: int
        """
        m = len(obstacleGrid)
        n = len(obstacleGrid[0])
        if obstacleGrid[0][0] == 1:
            return 0

        # Number of ways of reaching the starting cell = 1.
        obstacleGrid[0][0] = 1

        # Filling the values for the first column
        for i in range(1,m):
            obstacleGrid[i][0] = int(obstacleGrid[i][0] == 0 and obstacleGrid[i-1][0] == 1)

        # Filling the values for the first row        
        for j in range(1, n):
            obstacleGrid[0][j] = int(obstacleGrid[0][j] == 0 and obstacleGrid[0][j-1] == 1)

        # Starting from cell(1,1) fill up the values
        # No. of ways of reaching cell[i][j] = cell[i - 1][j] + cell[i][j - 1]
        # i.e. From above and left.
        for i in range(1,m):
            for j in range(1,n):
                if obstacleGrid[i][j] == 0:
                    obstacleGrid[i][j] = obstacleGrid[i-1][j] + obstacleGrid[i][j-1]
                else:
                    obstacleGrid[i][j] = 0

        # Return value stored in rightmost bottommost cell. That is the destination.            
        return obstacleGrid[m-1][n-1]
64. Minimum Path Sum (Medium)

Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.
Note: You can only move either down or right at any point in time.
Example:
Input:
[ [1,3,1],
[1,5,1],
[4,2,1] ]
Output: 7
Explanation: Because the path 1→3→1→1→1 minimizes the sum.

当前格子 = min(左,右) + grid[y][x]

* c++: Value of INT_MAX is +2147483647. Value of INT_MIN is -2147483648.

Solution 1: Recursion with memorization

def minPathSum(self, grid: List[List[int]]) -> int:
        # Recursion
        m = len(grid)
        n = len(grid[0])
        if m == 0: return 0
        s = [[0 for _ in range(n)] for i in range(m)]
        
        def minsum(grid, x, y, n, m):
            ans = 0
            if x == 0 and y == 0:
                return grid[y][x]
            if x < 0 or y < 0:
                return 22222222
            if s[y][x] > 0: 
                return s[y][x]
            ans = grid[y][x] + min(minsum(grid, x-1, y, n, m), minsum(grid, x, y-1, n, m))
            s[y][x] = ans
            return s[y][x]
        
        return minsum(grid, n-1, m-1, n, m)

Solution 2: DP

def minPathSum(self, grid: List[List[int]]) -> int:
        # DP
        m = len(grid)
        n = len(grid[0])
        if m == 0: return 0
        
        for i in range(m):
            for j in range(n):
                if i == 0 and j ==0:
                    continue
                if i == 0:
                    grid[i][j] += grid[i][j-1]
                elif j == 0:
                    grid[i][j] += grid[i-1][j]
                else:
                    grid[i][j] += min(grid[i][j-1], grid[i-1][j])
        
        return grid[m-1][n-1]
120. Triangle (Medium)

Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.
For example, given the following triangle
[ [2],
[3,4],
[6,5,7],
[4,1,8,3] ]
The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).

比如; ‘1’ 的最小值=min(6 或 5的最小值)

Solution 1 Top-Down DP:

def minimumTotal(self, triangle: List[List[int]]) -> int:
        n = len(triangle)
        f = [[0 for i in range(len(row))] for row in triangle]
        f[0][0] = triangle[0][0]
        
        for i in range(1, n):
            for j in range(len(triangle[i])):
                if j == 0:
                    # 最左边点的和 = 右上一点的和 + 当前点值 
                    f[i][j] = f[i-1][j] + triangle[i][j]
                elif j == len(triangle[i])-1:
                    # 最右一点的和 = 左上一点的和 + 当前点值
                    f[i][j] = f[i-1][j-1] + triangle[i][j]
                else:
                    # 其他点
                    f[i][j] = min(f[i-1][j], f[i-1][j-1]) + triangle[i][j]

        return min(f[-1])

Time:O(n^2); Space: O(n^2)
保留了所有行的访问记录,f[n]。当前行只和上一行有关,可使用滚动数组,f[0]和f[1]循环使用,每次用完swap。

Solution 2 不使用额外空间,复用triangle:

def minimumTotal(self, triangle: List[List[int]]) -> int:
        n = len(triangle)
        # triangle[i][j] := minTotal(i, j)
        # triangle[i][j] += min(triangle[i-1][j], triangle[i-1][j-1])
        
        for i in range(n):
            for j in range(i+1):
                if i==0 and j==0:
                    continue
                if j == 0:
                    triangle[i][j] += triangle[i-1][j]
                elif j==i:
                    triangle[i][j] += triangle[i-1][j-1]
                else:
                    triangle[i][j] += min(triangle[i-1][j], triangle[i-1][j-1])
        print(triangle)
        return min(triangle[n-1])
221. Maximal Square (Medium)

Given a 2D binary matrix filled with 0’s and 1’s, find the largest square containing only 1’s and return its area.
Example:
Input:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
Output: 4

Brute Force:

枚举所有子正方形矩阵,检查其中元素是否全0. 太复杂。

DP:
在这里插入图片描述
在这里插入图片描述

def maximalSquare(self, matrix: List[List[str]]) -> int:
        if matrix is None or len(matrix) < 1:
            return 0
        
        rows = len(matrix)
        cols = len(matrix[0])
        
        dp = [[0]*(cols+1) for _ in range(rows+1)]
        max_side = 0
        
        for r in range(rows):
            for c in range(cols):
                if matrix[r][c] == '1':
                    # 从左上角开始,右下方的值
                    dp[r+1][c+1] = min(dp[r][c], dp[r+1][c], dp[r][c+1]) + 1 
                    # Be careful of the indexing since dp grid has additional row and column
                    
                    max_side = max(max_side, dp[r+1][c+1])
                
        return max_side * max_side
304. Range Sum Query 2D - Immutable (Medium)

Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2).
在这里插入图片描述
The above rectangle (with the red border) is defined by (row1, col1) = (2, 1) and (row2, col2) = (4, 3), which contains sum = 8.
在这里插入图片描述
To get the sum of area D, get the Whole Area sum of area A,B,C,D first.
Then area D = Whole Area - area B - area C + area A

def __init__(self, matrix: List[List[int]]):
        if not matrix or not matrix[0]: return None
        row = len(matrix)
        col = len(matrix[0])
        self.dp = [[0] * (col+1) for _ in range(row+1)]
        
        # dp[i][j]: area of rectangle defined by (0, 0) and (i, j)
        for i in range(1, row+1):
            for j in range(1, col+1):
                self.dp[i][j] = self.dp[i][j-1]+self.dp[i-1][j]-self.dp[i-1][j-1]+matrix[i-1][j-1]

    def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
        return self.dp[row2+1][col2+1]-self.dp[row2+1][col1]-self.dp[row1][col2+1]+self.dp[row1][col1]
1277. Count Square Submatrices with All Ones (Medium)

Given a m * n matrix of ones and zeros, return how many square submatrices have all ones.
Example 1:
Input: matrix =
[
[0,1,1,1],
[1,1,1,1],
[0,1,1,1]
]
Output: 15
Explanation:
There are 10 squares of side 1.
There are 4 squares of side 2.
There is 1 square of side 3.
Total number of squares = 10 + 4 + 1 = 15.

class Solution:
    def countSquares(self, matrix: List[List[int]]) -> int:
        if matrix is None or len(matrix) == 0:
            return 0
        
        rows = len(matrix)
        cols = len(matrix[0])
        
        res = 0
        
        for r in range(rows):
            for c in range(cols):
                if matrix[r][c] == 1:
                    if r == 0 or c == 0:    #(0, 0) cell
                        res += 1
                    else:
                    # cell_val代表其所在‘1’ square的边长
                    # min:如果左、上、左上三个方向的邻居有一个0,不是square‘1’,保持原始matrix[r][c]值
                    #      如果邻居全是1,那么该cell value + 1,所在square‘1’边长 + 1
                        cell_val = min(matrix[r-1][c-1], matrix[r][c-1], matrix[r-1][c])\
                        + matrix[r][c]
                        res += cell_val
                        matrix[r][c] = cell_val #**memoize the updated result**
                
        return res

I: O(n), S = O(3n), T = O(3n)

198. House Robber (Easy)

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.
Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.
Example 1:
Input: nums = [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.

在这里插入图片描述
Solution 1 Recursion + Memorization:
自上而下

class Solution:
    def rob(self, nums: List[int]) -> int:
        # Recursion+memorization
        n = len(nums)
        r = [-1] * n
        
        def money(nums, i):
            if i < 0: return 0
            if r[i] > 0: return r[i]
            r[i] = max(money(nums, i-2) + nums[i], money(nums, i-1))
            return r[i]
        
        return money(nums, n-1)

Time Limit Exceeded
Solution 2 DP:
自下而上

def rob(self, nums: List[int]) -> int:
        # DP
        if not nums: return 0
        n = len(nums)
        dp = [0] * n
        if n == 1:
            return nums[0]
        for i in range(n):
            dp[i] = max(dp[i-2]+ nums[i], dp[i-1])

        return dp[-1]
213. House Robber II (Medium)

All houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.
Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.
Example 1:
Input: [2,3,2]
Output: 3
Explanation: You cannot rob house 1 (money = 2) and then rob house 3 (money = 2),
because they are adjacent houses.

第一家和最后一家不能同时被抢,所以最终抢劫最大值是抢劫第一个家和抢劫最后一家取最大值:rob(nums) = max(rob(nums[1:], nums[:-1])

class Solution:
    def rob(self, nums: List[int]) -> int:
        n = len(nums)
        if not nums: 
            return 0
        if n == 1: 
            return nums[0]
        if n == 2:
            return max(nums[0], nums[-1])

        dp = [0] * (n-1)
        def circrob(nums):
            for i in range(n-1):
                dp[i] = max(dp[i-2]+ nums[i], dp[i-1])
                # new_curr_max = max(prev_max + num, curr_max)
            print(dp)
            return dp[-1]
        
        # one list without last item
        cir1=circrob(nums[1:])
        dp = [0] * (n-1)
        # another list without first item
        cir2=circrob(nums[:-1])
        return max(cir1, cir2)
309. Best Time to Buy and Sell Stock with Cooldown (Medium)

Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day)
Example:
Input: [1,2,3,0,2]
Output: 3
Explanation: transactions = [buy, sell, cooldown, buy, sell]

给你每天的股价,没有交易次数限制,但是卖出后要休息一天才能再买进。最大收益是多少?
多个状态之间转换的DP。用状态转移图来辅助DP转移方程的获得。
在这里插入图片描述
XXX[i]: 代表某一行动在 i 天时可以获得的最大收益。

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        sold = 0
        rest = 0
        hold = -9999
        for price in prices:
            prev_sold = sold
            sold = hold + price
            hold = max(hold, rest - price)
            rest = max(rest, prev_sold)
            
        return max(rest, sold)
740. Delete and Earn (Medium)

Given an array nums of integers, you can perform operations on the array.
In each operation, you pick any nums[i] and delete it to earn nums[i] points. After, you must delete every element equal to nums[i] - 1 or nums[i] + 1.
You start with 0 points. Return the maximum number of points you can earn by applying such operations.
Example 1:
Input: nums = [3, 4, 2]
Output: 6
Explanation:
Delete 4 to earn 4 points, consequently 3 is also deleted.
Then, delete 2 to earn 2 points. 6 total points are earned.

Reduce the problem to House Robber Problem

Key observations: If we take nums[i]

  1. We can safely take all of its copies.
  2. We can’t take any of copies of nums[i – 1] and nums[i + 1] (be deleted).

This problem is reduced to 198 House Robber. Houses[i] has all the copies of num whose value is i.

[3 4 2] 排序-> [0 2 3 4], rob([0 2 3 4]) = 6
[2, 2, 3, 3, 3, 4] -> [0 22 33 4], rob([0 22 33 4]) = 9

Time complexity: O(n+r) reduction + O® solving rob = O(n + r)
Space complexity: O®
r = max(nums) – min(nums) + 1

class Solution(object):
    def deleteAndEarn(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if not nums: return 0
        r = max(nums)
        points = [0] * (r+1)
        for num in nums:
            # points[num]: 数字num的总和
            # 两个3->points[3] = 6
            points[num] += num
            # points: 一列告诉每个数字会给你多少points的值
        print(points)
        
        def rob(nums):
            # DP
            if not nums: return 0
            n = len(nums)
            dp = [0] * n
            if n == 1:
                return nums[0]
            for i in range(n):
                dp[i] = max(dp[i-2]+ nums[i], dp[i-1])

            return dp[-1]
        
        return rob(points)
790. Domino and Tromino Tiling (Medium)

有两种不同形状的骨牌(1×2长条形,L型)无限多块。给你一个2xN的板子,问一共有多少不同的方式可以完全覆盖。
在这里插入图片描述

def numTilings(self, N):
        """
        :type N: int
        :rtype: int
        """
        MOD = 10**9 + 7
        dp = [[0] * (3) for i in range(N+1)]
        dp[0][0] = dp[1][0]=1
        for i in range(2, N+1):
            dp[i][0] = (dp[i - 1][0] + dp[i - 2][0] + dp[i - 1][1] + dp[i - 1][2]) % MOD
            dp[i][1] = (dp[i - 2][0] + dp[i - 1][2]) % MOD
            dp[i][2] = (dp[i - 2][0] + dp[i - 1][1]) % MOD
        
        return dp[N][0]
801. Minimum Swaps To Make Sequences Increasing (Medium)

给你两个数组A, B。问最少需要多少次对应位置元素的交换才能使得A和B变成单调递增
在这里插入图片描述
只看最后两位,有四种情况。

def minSwap(self, A, B):
        """
        :type A: List[int]
        :type B: List[int]
        :rtype: int
        """
        n = len(A)
        # keep[i]: min swaps to make A, B increasing without swap A[i] and B[i] (last element)
        keep = [float('inf')] * n
        # swap[i]: min swaps to make A, B increasing with swap A[i] and B[i] (last element)
        swap = [float('inf')] * n
        keep[0] = 0
        swap[0] = 1
        
        for i in range(1, n):
            if A[i] > A[i-1] and B[i] > B[i-1]:
                keep[i] = keep[i-1] # no swap for both
                swap[i] = swap[i-1] + 1 # swap for both i-1 and i
            if B[i] > A[i-1] and A[i] > B[i-1]:
                swap[i] = min(swap[i], keep[i-1]+1) # swap i, keep i-1
                keep[i] = min(keep[i], swap[i-1])   # swap i-1, keep i
                
        return min(keep[-1], swap[-1])
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值