1. 题目背景与核心需求
P5020 [NOIP 2018 提高组] 货币系统是一道经典的动态规划问题,考察选手对完全背包问题的理解和应用能力。题目给定一个货币系统(n种面值的货币),要求我们找到一个等价的货币系统m,使得m的系统能够表示的所有金额与原始系统完全相同,且m尽可能小(即货币种类数最少)。
这个问题的实际意义在于货币系统的优化设计。比如现实中我们使用的硬币有1元、5元、10元等面值,这样的设计就是为了用最少数量的硬币种类满足日常交易需求。题目正是对这一现实问题的抽象和建模。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 问题分析与解题思路
2.1 问题转化与数学建模
首先我们需要明确几个关键概念:
- 货币系统的"表示能力":一个货币系统能表示的所有非负整数金额的集合
- 货币系统的"等价":两个货币系统能表示的金额集合完全相同
- 货币系统的"最小化":在所有等价的货币系统中,选择货币种类最少的那个
通过分析可以发现,题目要求的就是找出原始货币系统的"基"——即那些不能被系统中其他货币线性组合表示的面值。这些面值必须保留,而其他面值则可以去除。
2.2 算法选择与优化
这个问题可以转化为一个完全背包问题的变种。具体思路是:
- 对原始货币面值进行排序
- 从小到大检查每个面值是否能被比它小的面值组合表示
- 如果不能被表示,则必须保留该面值;否则可以去除
这个思路的正确性基于以下观察:
- 大面值的货币如果能由小面值的组合表示,那么它对于系统的表示能力没有贡献
- 我们需要保留的是那些"基础"面值,它们不能被其他面值组合表示
3. 代码实现与详细解析
3.1 基础实现代码
cpp复制#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN = 105;
const int MAXA = 25005;
int a[MAXN];
bool dp[MAXA];
int solve(int n) {
sort(a, a + n);
memset(dp, 0, sizeof(dp));
dp[0] = true;
int res = 0;
