異なる金種と合計金額のコインが与えられます。合計金額を構成するために必要なコインの最小数を計算する関数を記述します。コインの組み合わせで合計金額を構成できない場合は、-1を返します。
説明:各コインの数は無制限であると考えることができます。
リンク:https://leetcode-cn.com/problems/coin-change
問題解決の考え方:
コインの問題は、数学的に次の形式で表すことができます。
連続反復dp()では、n-coin == 0の場合、変更が交換され、1回追加されます。
再帰的な反復プロセスから考えると、反復ツリー表現を描くことができます。たとえば、amount = 9、coins = {1,2,5}と仮定します。
図の同じ色のカラーブロックは、反復プロセスで繰り返される計算の一部です。
方法1:再帰
再帰的アルゴリズムの時間の複雑さの分析:サブ問題の総数x各サブ問題の時間。
サブ問題の総数は、再帰ツリーノードの数です。これはわかりにくいです。O(n ^ k)です。つまり、指数関数的です。各サブ問題には、複雑さがO(k)のforループが含まれています。したがって、合計時間の複雑さはO(k * n ^ k)、指数レベルです。
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
def count(n):
# 基本情况的判定
if n==0 : return 0
if n<1 : return -1
# 初始化
min_coins=float('INF')
for coin in coins:
# 子问题的分解
subproblem=count(n-coin)
if subproblem == -1:
continue
#
min_coins=min(min_coins,1+subproblem)
# 如果一直continue,min_coins 不变,就代表没有可以分的可能
return min_coins if min_coins !=float('INF') else -1
return count(amount)
方法2:レコードテーブルの反復に参加する
「レコードテーブル」は、サブ質問の数を大幅に減らし(つまり、上の図の同じカラーブロックのいくつかの数値計算を置き換えます)、サブ質問の冗長性を完全に排除するため、サブ質問の総数は金額を超えませんn
。つまり、サブ質問の数は次のようになります。オン)。サブ問題を処理する時間は同じままですが、それでもO(k)であるため、合計時間の複雑さはO(kn)です。
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
memo={}
def count(n):
# 查找子问题的值
if n in memo:
return memo[n]
if n==0 : return 0
if n<1 : return -1
# 初始化
min_coins=float('INF')
for coin in coins:
# 子问题分解
subproblem=count(n-coin)
if subproblem == -1:
continue
min_coins=min(min_coins,1+subproblem)
# 存储子问题的解
memo[n]= min_coins if min_coins !=float('INF') else -1
return memo[n]
return count(amount)
方法3:リストを使用してdict中間テーブルの反復を置き換えます
1、2、3から「ボトムアップ」アプローチを採用します。。。nまで計算を開始します。
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
# 初始化结果集
# 每一个下标对应对应amount,下标对应的最后元素值就是零钱的最小个数
res=[float('INF')]*(amount+1)
res[0]=0
for i in range(amount+1):
for coin in coins:
# 不可分就下一轮
if (i-coin) <0: continue
# 对每一个i进行计算
res[i]=min(res[i],1+res[i-coin])
if res[amount] == float('INF'):
return -1
else:
return res[amount]
方法4:分割可能を使用してサイクル数を減らす
各再帰で、残りの量%nums [index]が0であるかどうかが判断されます。0である場合、現在のcoins [index]がその量を満たすことができることを意味し、現時点で最適なソリューションです。記録しない場合は、記録します。 、次に、必要なコイン[インデックス]の数を再帰的に決定するだけで済みます。それが最後のコイン[-1]までトラバースされ、金額を埋めることができない場合、解決策はありません。
最後に、非常に重要な剪定があります。現在のコインコイン[インデックス](全額)を入力すると、コインの数が前の結果よりも多くなり、後続のコインを判断し続ける必要はありません(コインがソートされているため)以上)。
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
n = len(coins)
coins.sort(reverse=True)
self.res = float("inf")
def dfs(index,target,count):
coin = coins[index]
if math.ceil(target/coin)+count>=self.res:
return
if target%coin==0:
self.res = count+target//coin
if index==n-1:return
print([range(target//coin,-1,-1)])
for j in range(target//coin,-1,-1):
dfs(index+1,target-j*coin,count+j)
dfs(0,amount,0)
return int(self.res) if self.res!=float("inf") else -1