Day 38 | 1049. 最後一塊石頭的重量 II 、494. 目標和 、474.一和零

forrestr發表於2024-07-07

1049. 最後一塊石頭的重量 II

本題就和 昨天的 416. 分割等和子集 很像了,可以嘗試先自己思考做一做。
影片講解:https://www.bilibili.com/video/BV14M411C7oV
https://programmercarl.com/1049.最後一塊石頭的重量II.html
有一堆石頭,每塊石頭的重量都是正整數。

每一回合,從中選出任意兩塊石頭,然後將它們一起粉碎。假設石頭的重量分別為 x 和 y,且 x <= y。那麼粉碎的可能結果如下:

如果 x == y,那麼兩塊石頭都會被完全粉碎;

如果 x != y,那麼重量為 x 的石頭將會完全粉碎,而重量為 y 的石頭新重量為 y-x。

最後,最多隻會剩下一塊石頭。返回此石頭最小的可能重量。如果沒有石頭剩下,就返回 0。

思考

石頭分為兩堆,每一堆的總量儘量接近sum/2。求容量為sum/2的揹包最大能裝的target,則最終剩餘為sum - 2*target
dp[i]表示 容量為i的揹包能不能裝滿

class Solution:
    def lastStoneWeightII(self, stones: List[int]) -> int:
        sum_ = sum(stones)
        bags = sum_//2
        #dp[i] 容量為i的揹包能不能裝滿
        dp = [False] * (bags+1)
        dp[0] = True
        for stone in stones:
            for i in range(bags,0,-1):
                if i >= stone:
                    dp[i] = dp[i] or dp[i-stone]
        for i in range(bags,-1,-1):
            if dp[i]:
                res = sum_ - 2 *i
                return res

494. 目標和

大家重點理解 遞推公式:dp[j] += dp[j - nums[i]],這個公式後面的提問 我們還會用到。
影片講解:https://www.bilibili.com/video/BV1o8411j73x
https://programmercarl.com/0494.目標和.html
給定一個非負整數陣列,a1, a2, ..., an, 和一個目標數,S。現在你有兩個符號 + 和 -。對於陣列中的任意一個整數,你都可以從 + 或 -中選擇一個符號新增在前面。

返回可以使最終陣列和為目標數 S 的所有新增符號的方法數。

思考

01揹包問題求組合個數,注意遞推公式和一些不滿足的情況提前返回。

class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        if (sum(nums) + target)%2 !=0:
            return 0 
        new_target = (sum(nums) + target)//2
        if new_target < 0 :
            return 0 
        # dp[i] 容量i的揹包裝滿有幾種方案
        dp = [0] * (new_target+1)
        dp[0] = 1
        for num in nums:
            for i in range(new_target,-1,-1):
                if i>=num:
                    dp[i] = dp[i]+dp[i-num]
        return dp[new_target]

474.一和零

透過這道題目,大家先粗略瞭解, 01揹包,完全揹包,多重揹包的區別,不過不用細扣,因為後面 對於 完全揹包,多重揹包 還有單獨講解。
影片講解:https://www.bilibili.com/video/BV1rW4y1x7ZQ
https://programmercarl.com/0474.一和零.html

給你一個二進位制字串陣列 strs 和兩個整數 m 和 n 。

請你找出並返回 strs 的最大子集的大小,該子集中 最多 有 m 個 0 和 n 個 1 。

如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。

示例 1:

輸入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3

輸出:4

解釋:最多有 5 個 0 和 3 個 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。 其他滿足題意但較小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不滿足題意,因為它含 4 個 1 ,大於 n 的值 3 。

思考

本題中strs 陣列裡的元素就是物品,每個物品都是一個!

而m 和 n相當於是一個揹包,兩個維度的揹包。

class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
        # dp[i][j] 容量為i個0和j個1的揹包可裝的最大子集個數
        dp = [[0] * (n+1) for _ in range(m+1)]
        dp[0][0] = 0
        def cal_01(s):
            nums = [0,0]
            for c in s:
                nums[int(c)]+=1
            return nums[0],nums[1]
        for str_ in strs:
            for i in range(m,-1,-1):
                for j in range(n,-1,-1):
                    zeronum,onenum = cal_01(str_)
                    if i>=zeronum and j>=onenum:
                        dp[i][j] = max(dp[i][j],dp[i-zeronum][j-onenum]+1)
        return dp[m][n] 

相關文章