leetcode 1187. Make Array Strictly Increasing [DP]

description

和最長遞增子序列(LIS)思路很像。
對於位置 i ,其取值要滿足單調遞增的約束,最多有兩種情況:

  1. arr1[i]
  2. 來自arr2

當位於i+1時,首先查看位置 i 的所有可能取值x,若arr1[i+1]大於x,則arr[i+1]滿足單調遞增且不會產生操作;若arr1[i+1]<=x,則arr[i+1]不滿足單調遞增。然後在arr2中尋找第一個大於x的值,該值滿足單調遞增,但會在x的基礎上增加一次操作。

這樣我們可以通過位置 i 來推出位置 i+1 。所以每個位置 i 需要一個字典dp,它的keys爲當前位置的可能取值,values爲對應取值的操作數。問題的解即是最後位置對應dp的values的最小值。

舉個例子:

Input: arr1 = [1,5,3,6,7], arr2 = [1,3,4]

位置 0 1 2 3 4
dp {1:0} {5:0, 3:1} {4:2} {6:2} {7:2}
含義 位置0可取0,需要0次操作 位置1可取5,需要0次操作
位置1可取3,需要1次操作
位置2可取4,需要2次操作 位置3可取6,需要2次操作 位置4可取7,需要2次操作
class Solution:
    def makeArrayIncreasing(self, arr1: List[int], arr2: List[int]) -> int:
        dp = {-1:0}
        arr2.sort()
        for i in arr1:
            tmp = collections.defaultdict(lambda: float('inf'))
            for key in dp:
                if i > key:
                    tmp[i] = min(tmp[i],dp[key])
                loc = bisect.bisect_right(arr2,key)
                if loc < len(arr2):
                    tmp[arr2[loc]] = min(tmp[arr2[loc]],dp[key]+1)
            dp = tmp
        if dp:
            return min(dp.values())
        return -1

還有一種將dp的key、value含義反轉的寫法(比上面快了4×),即key表示操作數,value表示取值。如果上一種寫法看懂了的話,應該也能看懂這個。

class Solution:
  def makeArrayIncreasing(self, arr1, arr2) -> int:
    N = len(arr1)
    arr2.sort()
    dp = {0: arr1[0], 1: arr2[0]}
    for i in range(1, N):
      tmp = collections.defaultdict(lambda: float('inf'))
      for k, v in dp.items():
        if v < arr1[i]:
          tmp[k] = min(tmp[k], arr1[i])
        p = bisect.bisect_right(arr2, v)
        if p < len(arr2):
          tmp[k + 1] = min(tmp[k + 1], arr2[p])
      dp = tmp
    if len(dp) == 0:
      return -1
    return min(dp.keys())
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章