1. 题目背景与核心理解
这道来自NOIP2018提高组的题目,题目编号P5020,要求我们解决一个关于货币系统的简化问题。题目描述的是:在一个国家有n种面额的货币系统,我们记为(n,a)。现在需要找到一个等价的货币系统(m,b),其中m尽可能小。这里的"等价"指的是两个系统能够表示的金额集合完全相同。
我第一次看到这个题目时,直觉上觉得这应该是个动态规划问题。但仔细分析后发现,它更像是一个数学问题与算法优化的结合体。题目中给出的货币系统(n,a)可以看作是一个向量空间,我们需要找到它的最小生成集。
举个例子,如果原始系统是(4,[3,9,10,6]),那么简化后的系统就是(2,[3,6])。因为9=3+6,10=3+6+1(但1不存在,所以这个例子可能不太恰当,更准确的例子应该是(3,[2,3,5])可以简化为(3,[2,3,5]),因为没有冗余元素)。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 解题思路分析
2.1 问题转化
经过分析,这个问题可以转化为:在原始货币系统中,找出所有"必要"的面额,即那些不能被其他面额线性组合表示的面额。换句话说,我们需要去除所有冗余的面额。
这实际上等价于在原始面额集合中寻找一个极大线性无关组。在数学上,这与计算向量空间的基的概念类似。
2.2 算法选择
解决这个问题最直接的方法是使用动态规划。具体步骤如下:
- 对原始面额数组进行排序
- 初始化一个布尔数组dp,表示每个金额是否能被表示
- 遍历每个面额,对于每个面额,检查它是否能被之前的面额表示
- 如果不能被表示,则将其加入结果集
这个方法的正确性基于以下观察:如果一个面额不能被比它小的面额组合表示,那么它必须被包含在简化系统中。
2.3 复杂度分析
假设最大面额为M,面额数量为n,那么:
- 排序时间复杂度:O(n log n)
- 动态规划部分:O(nM)
总体复杂度为O(n log n + nM)。考虑到题目中M≤25000,n≤100,这个复杂度是可以接受的。
3. C++实现详解
3.1 代码框架
cpp复制#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace st
