↓快轉到主要內容
  1. LeetCode/

LeetCode 4040: Minimum Operations to Form Subset Sum I

·2 分鐘· ·
LeetCode Medium Dynamic-Programming Knapsack Array
Wei Yi Chung
作者
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
目錄

基本資料
#

難易度: medium 第一次嘗試:2026-08-30

  • 提交紀錄:Accepted ×2

解題思路
#

一個數 x 先乘 a 次再除 b 次:

  • a >= b:結果是 x × 2^(a - b),只乘 a - b 次就能得到,步數更少。
  • a < b:結果是 x // 2^(b - a),只除 b - a 次就能得到,步數更少。

所以每個數的選項只有「只乘」和「只除」兩類:

  • x, 2x, 4x, ...(不超過 sum),步數 0, 1, 2, ...。
  • x // 2, x // 4, ...(大於 0),步數 1, 2, ...。

接著是分組 0/1 背包:每個數選一個選項或不選,dp[s] = 子集和剛好是 s 的最少步數。

我的解法
#

class Solution:
    def minOperations(self, nums: List[int], sum: int) -> int:
        INF = float("inf")
        dp = [0] + [INF] * sum                     # dp[s]:子集和為 s 的最少步數
        for x in nums:
            options = []
            v, cost = x, 0
            while v <= sum:                        # 只乘
                options.append((v, cost))
                v, cost = v * 2, cost + 1
            v, cost = x // 2, 1
            while v > 0:                           # 只除
                if v <= sum:
                    options.append((v, cost))
                v, cost = v // 2, cost + 1

            for s in range(sum, -1, -1):           # 倒著走:每個數最多用一次
                if dp[s] == INF:
                    continue
                for v, cost in options:
                    if s + v <= sum:
                        dp[s + v] = min(dp[s + v], dp[s] + cost)
        return dp[sum] if dp[sum] < INF else -1

這裡是「從 dp[s] 往 dp[s + v] 推」,s 倒著走時 dp[s] 一定還是上一個數處理完的值。

複雜度
#

時間 O(n · sum · log(sum)),空間 O(sum)。

要特別避免的錯誤
#

  • 以為要枚舉所有乘除的組合:先乘後除的組合都不會比單純的乘或除好。
  • 除到 0 的值沒有用,不用放進選項。

面試口說整理
#

先乘後除時,乘和除會互相抵銷,所以最佳做法只會是只乘或只除。我列出每個數能變成的值和步數,每個數最多選一個,做分組 0/1 背包,dp[s] 是子集和為 s 的最少步數,容量倒著走。時間 O(n · sum · log(sum))。

相關文章

LeetCode 4041: Minimum Operations to Form Subset Sum II
·2 分鐘
LeetCode Hard Dynamic-Programming Knapsack Breadth-First-Search Array
LeetCode 4041 解題筆記:乘除可以任意順序,用 BFS 找每個數能變成的所有值(不超過 sum)和最少步數,再做跟 4040 一樣的分組 0/1 背包
LeetCode 2585: Number of Ways to Earn Points
·1 分鐘
LeetCode Hard Dynamic-Programming Knapsack Array
LeetCode 2585 解題筆記:有數量上限的背包(分組背包);每種題目選 0 到 count 題,容量倒著走,在裡面枚舉選幾題
LeetCode 3660: Jump Game IX
·1 分鐘
LeetCode Medium Dynamic-Programming Array
LeetCode 3660 解題筆記:在 i 和 i+1 之間切一刀,如果左邊的最大值 > 右邊的最小值,兩邊就能互相跳到,答案一樣;否則左邊的答案就是左邊的最大值
LeetCode 3840: House Robber V
·1 分鐘
LeetCode Medium Dynamic-Programming Array
LeetCode 3840 解題筆記:只有相鄰而且同色才衝突;顏色不同直接拿,同色時才做打家劫舍的取捨
LeetCode 1493: Longest Subarray of 1's After Deleting One Element
·1 分鐘
LeetCode Medium Sliding-Window Dynamic-Programming Array
LeetCode 1493 解題筆記:找「最多 1 個 0」的最長視窗,因為一定要刪一個元素,答案是視窗長度減 1
LeetCode 416: Partition Equal Subset Sum
·3 分鐘
LeetCode Medium Dynamic-Programming Knapsack Subset-Sum
LeetCode 416 解題筆記,依照原始 learning note 重新整理