完全背包 DP:dp[i] 从所有硬币面额转移,取最小值,不可达用哨兵标记。
OJ: leetcodecn
题目 ID: coin-change
难度:普及+/提高
标签:动态规划完全背包
日期: 2026-07-29 12:38
题意
给定硬币面额和金额,求最少硬币数。每种硬币无限使用。
思路
dp[i] 表示凑成金额 i 的最少硬币数。dp[i] = min(dp[i-c] + 1) 对所有 c <= i。初值 dp[0] = 0,其余 amount + 1(不可达哨兵)。最终 dp[amount] > amount 则无解。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
int coinChange(vector<int> &coins, int amount) {
vector<int> dp(amount + 1, amount + 1);
dp[0] = 0;
for (int i = 1; i <= amount; i++)
for (int c : coins)
if (i >= c)
dp[i] = min(dp[i], dp[i - c] + 1);
return dp[amount] > amount ? -1 : dp[amount];
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, amt;
cin >> n >> amt;
vector<int> a(n);
for (int &x : a)
cin >> x;
cout << Solution().coinChange(a, amt) << '\n';
return 0;
}python
#!/usr/bin/env python3
from typing import List
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
dp = [amount + 1] * (amount + 1)
dp[0] = 0
for i in range(1, amount + 1):
for c in coins:
if i >= c:
dp[i] = min(dp[i], dp[i - c] + 1)
return -1 if dp[amount] > amount else dp[amount]
def main():
n, amt = map(int, input().split())
a = list(map(int, input().split()))
print(Solution().coinChange(a, amt))
if __name__ == "__main__":
main()复杂度
- 时间复杂度:
。 - 空间复杂度:
。
总结
零钱兑换是完全背包求最少物品数的经典题。不可达哨兵用 amount + 1(而非 INF),因为最多用 amount 个 1 元硬币。