题目
传送门: DPL_1_A: Coin Changing Problem
概述
使用面额为 d 1 d_{1} d1, d 2 d_{2} d2, …, d m d_{m} dm的硬币找出可以凑出 n n n美分的最小硬币数. 硬币可以使用任意次数
输入
n n n m m m
d 1 d_{1} d1 d 2 d_{2} d2 . . . ... ... d m d_{m} dm
两个整数 n n n和 m m m在第一行中给出, 可以使用的硬币面额在第二行中给出
输出
输出最小的硬币数
约束条件
- 1 ≤ n ≤ 50000 1 \leq n \leq 50000 1≤n≤50000
- 1 ≤ m ≤ 20 1 \leq m \leq 20 1≤m≤20
- 1 ≤ 硬 币 面 额 ≤ 10000 1 \leq 硬币面额 \leq 10000 1≤硬币面额≤10000
- 所有的硬币面额都不相同, 绝对包含面额为1的硬币
样例输入1
55 4
1 5 10 50
样例输出1
2
样例输入2
15 6
1 2 7 8 12 50
样例输出2
2
样例输入3
65 6
1 2 7 8 12 50
样例输出3
3
求解
分析
第一眼看成贪心问题, 不过想着它放在动态规划的题目中, 至少要用动态规划的思路去解题, 然后还是以贪心的原理设计动态规划的关系式的.
dp[x] 表示x美分至少需要的硬币数量, 那么就有dp[0] = 0; dp[i] = dp[i - D[j]] + 1; 表示0美分需要0个硬币, D[j]表示小于等于i的最大硬币面额, 然后i美分就等于i-D[j]的硬币数加上1个硬币(面额D[j]), 然后在第二个样例输入中果断出错.
分析, 发现按照我的思路, 那么15 = 12 + 2 + 1, 而正确结果是15 = 8 + 7, 所以我在第二个关系式中, 不应该固定D[j]的值, 而是应该取所有面额小于等于i的硬币进行计算, 找出最小的一个.
dp[i] 先赋值一个最大值, 然后在所有的小于i的硬币中进行处理 dp[i] = min(dp[i], dp[i - D[j]] + 1);
设计
dp[x] 表示x美分最少需要的硬币个数, 它具有如下关系式
dp[0] = 0; // 0美分需要0个硬币
dp[i] = min(dp[i], dp[i - D[j]] + 1); // i美分需要min{ i-D[j]美分的硬币数+1 } 举个例子 样例2中, dp[15], 可以使用dp[15 - 12] + 1 = dp[3] + 1 = 3; 也可以使用dp[15 - 8] + 1 = dp[7] + 1 = 2; 等等, 找到最小的硬币数量
编码
#include <bits/stdc++.h>
using namespace std;
#define MAX_N 50005
#define MAX_M 25
int n, m;
int D[MAX_M];
int dp[MAX_N];
int main(void) {
scanf("%d %d", &n, &m);
for (int i = 0; i < m; i++) {
scanf("%d", &D[i]);
}
sort(D, D + m, std::greater<int>());
dp[0] = 0;
int i, j;
for (i = 1; i <= n; i++) {
dp[i] = INT_MAX;
for (j = 0; j < m; j++) {
if (i >= D[j])
dp[i] = min(dp[i - D[j]] + 1, dp[i]);
}
}
printf("%d\n", dp[n]);
}
结果
总结
不要第一眼看上去挺简单就不去分析样例, 不然会做很多无用功的