题目## 题目
解题思路
这是一道动态规划求解零钱兑换组合数的问题,主要思路如下:
-
问题分析:
- 给定6种面额:1、5、10、20、50、100元
- 每种面额的数量无限
- 求组成目标金额 n n n 的不同组合数
-
解决方案:
- 使用动态规划求解
- d p [ i ] dp[i] dp[i] 表示组成金额 i i i 的组合数
- 对每种面额
c
o
i
n
s
[
j
]
coins[j]
coins[j],有:
d p [ i ] + = d p [ i − c o i n s [ j ] ] dp[i] += dp[i - coins[j]] dp[i]+=dp[i−coins[j]]
-
关键点:
- 初始化 d p [ 0 ] = 1 dp[0] = 1 dp[0]=1
- 外层循环遍历面额,内层循环遍历金额
- 注意使用 long 类型避免溢出
代码
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
// 可用的面额
int coins[6] = {1, 5, 10, 20, 50, 100};
while (cin >> n) {
// 创建dp数组并初始化
vector<long> dp(n + 1, 0);
dp[0] = 1; // 金额为0的组合数为1
// 对每种面额进行处理
for (int i = 0; i < 6; i++) {
// 从coins[i]开始,避免负数索引
for (int j = coins[i]; j <= n; j++) {
dp[j] += dp[j - coins[i]];
}
}
cout << dp[n] << endl;
}
return 0;
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 可用的面额
int[] coins = {1, 5, 10, 20, 50, 100};
while (sc.hasNext()) {
int n = sc.nextInt();
// 创建dp数组并初始化
long[] dp = new long[n + 1];
dp[0] = 1; // 金额为0的组合数为1
// 对每种面额进行处理
for (int coin : coins) {
// 从coin开始,避免负数索引
for (int j = coin; j <= n; j++) {
dp[j] += dp[j - coin];
}
}
System.out.println(dp[n]);
}
}
}
def count_combinations(n: int) -> int:
# 可用的面额
coins = [1, 5, 10, 20, 50, 100]
# 创建dp数组并初始化
dp = [0] * (n + 1)
dp[0] = 1 # 金额为0的组合数为1
# 对每种面额进行处理
for coin in coins:
# 从coin开始,避免负数索引
for j in range(coin, n + 1):
dp[j] += dp[j - coin]
return dp[n]
# 处理输入
while True:
try:
n = int(input())
print(count_combinations(n))
except EOFError:
break
算法及复杂度
- 算法:动态规划
- 时间复杂度: O ( n ) \mathcal{O}(n) O(n) - n n n 为目标金额
- 空间复杂度: O ( n ) \mathcal{O}(n) O(n) - 需要一个长度为 n + 1 n+1 n+1 的 dp 数组