這一部分,我們將講解DP問題求解combine sum的最優解問題。
涉及的代碼題目是leetcode 377、322
leetcode 377
問題描述:整數數組,無重複元素,但每個數字可以被重複使用,給出組合的總數,無須給出所有排列。
算法設計思路:創建dp數組,dp[i]表示target爲i時,無重複元素組合的總數。
算法實現:
class Solution(object):
def combineSolution4(self, nums, target):
nums.sort()
dp = [0] * (target+1)
dp[0] = 1
for i in range(1, target+1):
for num in nums:
if i >= num:
dp[i] = dp[i] + dp[i-num]
else: # 剪枝
break
result = dp[target]
return result
demo測試以及結果:
if __name__ == '__main__':
s = Solution()
print(s.combineSolution4([1,2,3], 4))
7
leetcode 322
問題描述:
已知不同面值的鈔票,求如何使用最少數量的鈔票組成某個金額,鈔票可重複使用。若任意數量的已知面值都無法組成該金額,直接返回0。舉例如下:
鈔票面值 [1,2,5],target=11,解=3(5+5+1)
鈔票面值 [2],target=3,解=0(無法組成)
鈔票面值 [1,2,5,7,10],target=14,解=2(7+7)
算法設計思路:
對於[1, 2, 5],target=11的問題,可用貪心策略求解得到最優解3,但是對於[1, 2, 5, 7, 10],target=14用貪心策略的結果3(10+2+2)就不是全局最優解,此時DP策略可以解決,創建dp數組,dp[i]表示組成金額i的最少鈔票數量。
對於[1, 2, 5, 7, 10],target=14這個instance來說,dp[i]代表最優解,初始值都是-1(dp[0]——dp[14]=-1),計算dp[i]時,dp[0]~dp[i-1]都已經計算出來,它們之間的遞推關係如何,嘗試使用這些值遞推。
dp[i-1] 與 coin[0] (1)組合;
dp[i-2] 與 coin[1] (2)組合;
dp[i-5] 與 coin[2] (5)組合;
dp[i-7] 與 coin[3] (7)組合;
dp[i-10] 與 coin[4] (10)組合
而狀態i可由前面這5個狀態(i-1,i-2,i-5,i-7,i-10)共同決定,即:
dp[i] = min{dp[i-1], dp[i-2], dp[i-5], dp[i-7], dp[i-10]}+1
算法實現:
def money_change(nums, target):
dp = [0] * (target+1) # dp
for i in range(1, target+1):
min = target
for num in nums:
if i >= num and dp[i-num] < min:
min = dp[i-num]
dp[i] = min+1
return dp[target]
demo測試以及結果:
if __name__ == '__main__':
r = money_change(nums=[1,2,5,7,10], target=14)
print(r)
r = money_change(nums=[1,2,5,7,10], target=18)
print(r)
2
3