这一部分,我们将讲解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