AcWing 532. 货币系统

题目传送门

一、题目解析

感觉这道题重点不是 \(DP\) 而是证明……很多高赞题解都是直接上结论的……,学习算法,个人感觉还是把理论基础打牢才靠谱

比如你考场的时候,想到了这个解法,如果用了怕结论是错的,得分还不如部分分;不用又怕错失正解,其实是很纠结的……所以我这里就写一写证明过程吧,其实也不难。

Statement


有 nn 种不同面值的货币,第 ii 种面值为 a[i]a[i] ,每种无限张。

将一个有 nn 种货币、面值数组为 a[1…n]a[1…n] 的货币系统记作 (n,a)(n,a) .称两个货币系统 (n,a),(m,b)(n,a),(m,b) 等价,当且仅当对于任意 x∈Nx∈N ,要么均能被两个货币系统表示出来,要么不能被任何一个表示。

给定一个货币系统 (n,a)(n,a) ,找到一个最小的 mm 使得存在货币系统 (m,b)(m,b) 与 (n,a)(n,a) 等价。

n≤100,a[i]≤25000n≤100,a[i]≤25000 .

#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--) {
        cin >> n;
        for (int i = 0; i < n; i++)cin >> v[i];
        //每个货币的金额,都只能由比它小的货币组装而成,需要排一下序
        sort(v, v + n);
        //背包容量
        int m = v[n - 1];
        //每轮初始化一次dp数组,因为有多轮
        memset(f, 0, sizeof f);
        //在总金额是0的情况下,只有一种方案
        f[0] = 1;

        //利用01背包计算每个体积(面额)的组成方案
        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);
    }
}

上一篇:MFC 文件操作


下一篇:C++ Primer----一个关于 vector 的有趣的问题