You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4.2 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

##AcWing 532. 货币系统

一、题目描述

在网友的国度中共有 n 种不同面额的货币,第 i 种货币的面额为 a[i],你可以假设每一种货币都有无穷多张。

为了方便,我们把货币种数为 n、面额数组为 a[1..n] 的货币系统记作 (n,a)。 

在一个完善的货币系统中,每一个非负整数的金额 x 都应该可以被表示出,即对每一个非负整数 x,都存在 n 个非负整数 t[i] 满足 a[i]×t[i] 的和为 x

然而,在网友的国度中,货币系统可能是不完善的,即可能存在金额 x 不能被该货币系统表示出。

例如在货币系统 n=3,a=[2,5,9] 中,金额 1,3 就无法被表示出来。 

两个货币系统 (n,a) 和 (m,b) 是等价的,当且仅当对于任意非负整数 x,它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。 

现在网友们打算 简化一下 货币系统。

他们希望找到一个货币系统 (m,b),满足 (m,b) 与原来的货币系统 (n,a) 等价,且 m 尽可能的小。

他们希望你来协助完成这个艰巨的任务:找到最小的 m

输入格式 输入文件的第一行包含一个整数 T,表示数据的组数。

接下来按照如下格式分别给出 T 组数据。 

每组数据的第一行包含一个正整数 n

接下来一行包含 n 个由空格隔开的正整数 a[i]

输出格式 输出文件共有 T 行,对于每组数据,输出一行一个正整数,表示所有与 (n,a) 等价的货币系统 (m,b) 中,最小的 m

数据范围 1≤n≤100,1≤a[i]≤25000,1≤T≤20

输入样例

2 
4 
3 19 10 6 
5 
11 29 13 19 17 

输出样例

2
5

二、题目解析

大写的 简化 !明显在提示我们可以只简化,不用考虑替换成其它的货币金额啊!如果只是简化,那么简单:

  • 将货币面额排序(因为给的面额是无序的)

  • 每一个面额考查它能不能被它之前的面额描述出来,如果能,它就没有存在的必要。将这类的货币从系统中去除就可以得到等价的最小数量货币系统。

可以用dp求出能表示该面额的方案数,若对于一张货币方案数唯一(即只能被自己表示),则这张货币不能被省略,反之可以被省略,最后统计一下就行了。

### 三、完全背包+求方案数

#include <bits/stdc++.h>

using namespace std;
const int N = 110;   // N个正整数
const int M = 25010; // 表示的最大金额上限
int n;               // 实际输入的正整数个数
int v[N];            // 每个输入的数字,也相当于占用的体积是多大
int f[M];            // 二维优化为一维的DP数组f[i]面额为i时的前序货币组成方案数

int main() {
    int T;
    cin >> T;
    while (T--) {
        // 每轮初始化一次dp数组因为有多轮
        memset(f, 0, sizeof f);

        cin >> n;
        for (int i = 0; i < n; i++) cin >> v[i];
        // 每个货币的金额,都只能由比它小的货币组装而成,需要排一下序
        sort(v, v + n);

        // 背包容量
        int m = v[n - 1];

        // 在总金额是0的情况下只有一种方案
        f[0] = 1;
        
        // 恰好装满:计算每个体积(面额)的组成方案
        for (int i = 0; i < n; i++)
            for (int j = v[i]; j <= m; j++)
                f[j] += f[j - v[i]];

        // 统计结果数
        int res = 0;
        for (int i = 0; i < n; i++)
            // 如果当前面额的组成方案只有一种,那么它只能被用自己描述自己,不能让其它人描述自己
            // 这个面额就必须保留
            if (f[v[i]] == 1) res++;
        // 输出结果
        printf("%d\n", res);
    }
    return 0;
}