欢迎大家订阅我的专栏:算法题解:C++与Python实现!
本专栏旨在帮助大家从基础到进阶 ,逐步提升编程能力,助力信息学竞赛备战!
专栏特色
1.经典算法练习:根据信息学竞赛大纲,精心挑选经典算法题目,提供清晰的代码实现与详细指导,帮助您夯实算法基础。
2.系统化学习路径:按照算法类别和难度分级,从基础到进阶,循序渐进,帮助您全面提升编程能力与算法思维。
适合人群:
附上汇总贴:历年CSP-S复赛真题解析 | 汇总
【题目来源】
洛谷:P5020 [NOIP 2018 提高组] 货币系统 - 洛谷 (luogu.com.cn)
【题目描述】
在网友的国度中共有 n n n 种不同面额的货币,第 i i i 种货币的面额为 a [ i ] a[i] a[i],你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为 n n n、面额数组为 a [ 1.. n ] a[1..n] a[1..n] 的货币系统记作 ( n , a ) (n,a) (n,a)。
在一个完善的货币系统中,每一个非负整数的金额 x x x 都应该可以被表示出,即对每一个非负整数 x x x,都存在 n n n 个非负整数 t [ i ] t[i] t[i] 满足 a [ i ] × t [ i ] a[i] \times t[i] a[i]×t[i] 的和为 x x x。然而, 在网友的国度中,货币系统可能是不完善的,即可能存在金额 x x x 不能被该货币系统表示出。例如在货币系统 n = 3 n=3 n=3, a = [ 2 , 5 , 9 ] a=[2,5,9] a=[2,5,9] 中,金额 1 , 3 1,3 1,3 就无法被表示出来。
两个货币系统 ( n , a ) (n,a) (n,a) 和 ( m , b ) (m,b) (m,b) 是等价的,当且仅当对于任意非负整数 x x x,它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。
现在网友们打算简化一下货币系统。他们希望找到一个货币系统 ( m , b ) (m,b) (m,b),满足 ( m , b ) (m,b) (m,b) 与原来的货币系统 ( n , a ) (n,a) (n,a) 等价,且 m m m 尽可能的小。他们希望你来协助完成这个艰巨的任务:找到最小的 m m m。
【输入】
输入文件的第一行包含一个整数 T T T,表示数据的组数。
接下来按照如下格式分别给出 T T T 组数据。 每组数据的第一行包含一个正整数 n n n。接下来一行包含 n n n 个由空格隔开的正整数 a [ i ] a[i] a[i]。
【输出】
输出文件共有 T T T 行,对于每组数据,输出一行一个正整数,表示所有与 ( n , a ) (n,a) (n,a) 等价的货币系统 ( m , b ) (m,b) (m,b) 中,最小的 m m m。
【输入样例】
2
4
3 19 10 6
5
11 29 13 19 17
【输出样例】
2
5
【算法标签】
《洛谷 P5020 货币系统》 #动态规划,dp# #数学# #贪心# #背包# #NOIP提高组# #2018#
【代码详解】
#include
using namespace std;
int T, n, a[105], f[25005], ans; // T: 测试用例数, n: 货币种类数, a: 货币面值数组
// f: 动态规划数组, ans: 结果计数器
int main()
{
// 输入测试用例数
cin >> T;
// 处理每个测试用例
while (T--) {
// 输入货币种类数
cin >> n;
// 输入货币面值
for (int i = 1; i <= n; i++)
cin >> a[i];
// 将货币面值排序(从小到大)
sort(a + 1, a + n + 1);
// 初始化动态规划数组(完全背包问题)
memset(f, 0, sizeof(f));
f[0] = 1; // 金额为0时有一种方案(不选任何货币)
// 完全背包动态规划处理
for (int i = 1; i <= n; i++) {
for (int j = a[i]; j <= a[n]; j++) {
f[j] = f[j] + f[j - a[i]]; // 状态转移方程
}
}
// 统计不能被其他货币组合表示的面值数量
ans = 0;
for (int i = 1; i <= n; i++)
if (f[a[i]] == 1) // 如果该面值只能由自己表示(方案数为1)
ans++;
// 输出结果
cout << ans << endl;
}
return 0;
}
【运行结果】
2
4
3 19 10 6
2
5
11 29 13 19 17
5