Day 42 DP:背包問題牵舵,416. 分割等和子集

背包問題介紹

  • 參考

    • 背包:最大容量(體積妒蛇,重量)j
    • 物品:
      • 體積,重量 weight[i]
      • 價值 value[i]
    • 以下介紹0-1背包:每個物品最多只能使用一次帽氓。
    • dp[i][j]: 物品0,1,...,i中取若干個; 背包容量為j趣斤;最大價值
    • 遞推公式:
      • 第i個物品:不取 vs 取 (注意要先判斷是否可取)
      • dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i]), j >= weight[i]
    • dp[i][0] = 0
      dp[0][j] = 0, j < weight[0]
      dp[0][j] = value[0], j >= weight[0]
    • 遍歷順序
      • 先物品,再背包 (組合杏节,帶去重功能)
      • 先背包唬渗,再物品 (排列)
def test_2_wei_bag_problem1(bag_size, weight, value) -> int: 
    rows, cols = len(weight), bag_size + 1
    dp = [[0 for _ in range(cols)] for _ in range(rows)]
    
    # 初始化dp數(shù)組. 
    for i in range(rows): 
        dp[i][0] = 0
    first_item_weight, first_item_value = weight[0], value[0]
    for j in range(1, cols):    
        if first_item_weight <= j: 
            dp[0][j] = first_item_value

    # 更新dp數(shù)組: 先遍歷物品, 再遍歷背包. 
    for i in range(1, len(weight)): 
        cur_weight, cur_val = weight[i], value[i]
        for j in range(1, cols): 
            if cur_weight > j: # 說明背包裝不下當前物品. 
                dp[i][j] = dp[i - 1][j] # 所以不裝當前物品. 
            else: 
                # 定義dp數(shù)組: dp[i][j] 前i個物品里,放進容量為j的背包奋渔,價值總和最大是多少镊逝。
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - cur_weight] + cur_val)

    print(dp)


if __name__ == "__main__": 
    bag_size = 4
    weight = [1, 3, 4]
    value = [15, 20, 30]
    test_2_wei_bag_problem1(bag_size, weight, value)
  • 空間優(yōu)化
    • dp[i][j]由dp[i-1][j]和dp[i-1][j-weight[i]]得到.物品空間維數(shù)可壓縮 (滾動數(shù)組)。
    • dp[j]: 容量為j的背包(當前)所背物品的最大價值
    • 每個物品只能用一次嫉鲸,遍歷j需要倒序
def test_1_wei_bag_problem():
    weight = [1, 3, 4]
    value = [15, 20, 30]
    bag_weight = 4
    # 初始化: 全為0
    dp = [0] * (bag_weight + 1)

    # 先遍歷物品, 再遍歷背包容量
    for i in range(len(weight)):
        for j in range(bag_weight, weight[i] - 1, -1):
            # 遞歸公式
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i])

    print(dp)

test_1_wei_bag_problem()

416. 分割等和子集

  • 思路
    • example
    • 背包:容量target = sum_ //2 (sum_為偶數(shù))
    • 物品:數(shù)字
      • 重量=nums[i]
      • 價值=nums[i]
    • 0-1背包(每個數(shù)字使用一次)
    • dp[i][j]: 數(shù)字nums[0],...,nums[i]中選取若干個撑蒜,背包容量為target所能容納的最大價值(子集和)。
    • 目標:return dp[n-1][target] == target
    • 遞推公式:

dp[i][j] = max(dp[i-1][j], dp[i-1][j-nums[i]] + nums[j])

  • 復雜度. 時間:O(n*max), 空間: O(max), max = \max(nums)
class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        n = len(nums)
        sum_ = sum(nums)
        if sum_ % 2 != 0:
            return False 
        target = sum_ // 2
        dp = [[0 for _ in range(target+1)] for _ in range(n)]
        for j in range(nums[0], target+1):
            dp[0][j] = nums[0]
        for i in range(1, n):
            for j in range(1, target+1):
                if j < nums[i]:
                    dp[i][j] = dp[i-1][j]
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i-1][j-nums[i]] + nums[i])
        return dp[n-1][target] == target  
class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        n = len(nums) 
        sum_ = sum(nums)
        if sum_ % 2 != 0:
            return False 
        target = sum_ // 2 
        dp = [[0 for _ in range(target+1)] for _ in range(n)]  
        for j in range(target+1):
            if j >= nums[0]:
                dp[0][j] = nums[0]  
        for i in range(n):
            for j in range(target+1):
                if j < nums[i]:
                    dp[i][j] = dp[i-1][j] 
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i-1][j-nums[i]]+nums[i])
        return dp[n-1][target] == target  
  • 空間優(yōu)化(內(nèi)層背包逆序遍歷)
class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        n = len(nums)
        sum_ = sum(nums)
        if sum_ % 2 != 0:
            return False 
        target = sum_ // 2
        dp = [0 for _ in range(target+1)]
        for j in range(nums[0], target+1):
            dp[j] = nums[0]
        for i in range(1, n):
            for j in range(target, 0, -1):
                if j >= nums[i]:
                    dp[j] = max(dp[j], dp[j-nums[i]] + nums[i])
        return dp[target] == target
  • 可只記錄dp[i][j]的True/False值(可行性), 下面給出二維DP版本
    • 注意dp[i][j]關于背包容量j這里是“=”邏輯玄渗, 不是最多邏輯座菠。
class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        n = len(nums) 
        sum_ =sum(nums)
        if sum_ % 2 != 0:
            return False  
        target = sum_ // 2
        dp = [[False for _ in range(target+1)] for _ in range(n)]
        for j in range(target+1):
            if j == nums[0]:
                dp[0][j] = True 
                break  
        for i in range(1, n):
            for j in range(target+1):
                if j < nums[i]:
                    dp[i][j] = dp[i-1][j] 
                else:
                    dp[i][j] = dp[i-1][j] or dp[i-1][j-nums[i]]
        return dp[n-1][target]  
  • dfs, 記億化dfs
TBA
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市藤树,隨后出現(xiàn)的幾起案子浴滴,更是在濱河造成了極大的恐慌,老刑警劉巖岁钓,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件升略,死亡現(xiàn)場離奇詭異,居然都是意外死亡屡限,警方通過查閱死者的電腦和手機品嚣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钧大,“玉大人翰撑,你說我怎么就攤上這事“⊙耄” “怎么了眶诈?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵涨醋,是天一觀的道長。 經(jīng)常有香客問我册养,道長东帅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任球拦,我火速辦了婚禮,結(jié)果婚禮上帐我,老公的妹妹穿的比我還像新娘坎炼。我一直安慰自己,他們只是感情好拦键,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布谣光。 她就那樣靜靜地躺著,像睡著了一般芬为。 火紅的嫁衣襯著肌膚如雪萄金。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天媚朦,我揣著相機與錄音氧敢,去河邊找鬼。 笑死询张,一個胖子當著我的面吹牛孙乖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播份氧,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼唯袄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蜗帜?” 一聲冷哼從身側(cè)響起恋拷,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎厅缺,沒想到半個月后蔬顾,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡店归,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年阎抒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片消痛。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡且叁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出秩伞,到底是詐尸還是另有隱情逞带,我是刑警寧澤欺矫,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站展氓,受9級特大地震影響穆趴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜遇汞,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一未妹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧空入,春花似錦络它、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至埋凯,卻和暖如春点楼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背白对。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工掠廓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人躏结。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓却盘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親媳拴。 傳聞我的和親對象是個殘疾皇子黄橘,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容