历年CSP-S复赛真题解析 | 2018年T2 货币系统

​欢迎大家订阅我的专栏:算法题解:C++与Python实现!
本专栏旨在帮助大家从基础到进阶 ,逐步提升编程能力,助力信息学竞赛备战!

专栏特色
1.经典算法练习:根据信息学竞赛大纲,精心挑选经典算法题目,提供清晰的代码实现与详细指导,帮助您夯实算法基础。
2.系统化学习路径:按照算法类别和难度分级,从基础到进阶,循序渐进,帮助您全面提升编程能力与算法思维。

适合人群:

  • 准备参加蓝桥杯、GESP、CSP-J、CSP-S等信息学竞赛的学生
  • 希望系统学习C++/Python编程的初学者
  • 想要提升算法与编程能力的编程爱好者

附上汇总贴:历年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

你可能感兴趣的:(算法,c++)