1 条题解
-
0
货币系统详细题解
问题描述
在网友的国度中共有 种不同面额的货币,第 种货币的面额为 ,每种货币都有无穷多张。货币系统记作 。两个货币系统等价当且仅当它们能表示的非负整数金额完全相同。现需找到与原货币系统等价且货币种数 最小的系统,求最小 。
算法思路
核心在于筛选出「必要货币」—— 即无法被其他货币组合表示的货币。具体步骤:
- 排序处理:将货币面额从小到大排序,确保较小面额先被处理(小面额可能是大面额的组成基础)。
- 动态规划标记可表示金额:用布尔数组 标记已能表示的金额( 表示金额 可被当前筛选出的货币表示)。
- 筛选冗余货币:对每个面额,若其已能被之前的货币表示(),则为冗余货币,不计入结果;否则为必要货币,并用其更新 数组,标记更多可表示的金额。
代码解析
#include <bits/stdc++.h> using namespace std; int t; int main() { // freopen("money.in", "r", stdin); // freopen("money.out", "w", stdout); cin >> t; // 读取测试组数 while (t--) { int n; cin >> n; // 货币种数 int A[n + 1]; // 存储货币面额 bool F[25001] = {true}; // F[x]标记金额x是否可被表示,初始F[0]=true(0元可表示) for (int i = 1; i <= n; i++) { cin >> A[i]; // 读取面额 } sort(A + 1, A + n + 1); // 面额从小到大排序 int ans = n; // 初始假设所有货币都是必要的 for (int i = 1; i <= n; i++) { // 若当前货币已能被之前的货币表示,则为冗余货币 if (F[A[i]]) { ans--; } else { // 用当前货币更新可表示的金额 for (int j = A[i]; j <= A[n]; j++) { F[j] |= F[j - A[i]]; // 若j-A[i]可表示,则j可表示 } } } cout << ans << endl; // 输出必要货币的数量 } return 0; }
关键逻辑说明
- 排序的作用:从小到大处理货币,确保判断某货币是否冗余时,所有可能组成它的小面额货币已被考虑。
- F数组的意义: 为 表示金额 可被当前保留的必要货币组合表示。初始时仅 (0元无需任何货币即可表示)。
- 冗余判断:对于排序后的第 个货币 ,若 ,说明它可被之前的货币表示,是冗余的,减少结果计数;否则,用它更新 数组,标记所有能通过它扩展的可表示金额。
样例分析
第一组数据
- 输入:,面额
- 排序后:
- 处理过程:
- : 初始为 (非冗余),用3更新 ,标记 可表示。
- : 已为 (可被3表示),冗余,。
- : 为 (非冗余),用10更新 ,标记 可表示。
- : 为 (),冗余,。
- 输出:
第二组数据
- 输入:,面额
- 排序后:
- 处理过程:每个货币均无法被之前的货币表示(如13不能被11表示,17不能被11+13表示等),均为必要货币。
- 输出:
复杂度分析
- 时间复杂度:每组数据中,排序为 ;遍历货币并更新 数组的总复杂度为 ( 为最大面额)。整体为 。
- 空间复杂度:,主要为 数组的空间(大小为最大面额 )。
算法标签
- 动态规划
- 贪心算法
- 货币系统优化
- 冗余元素筛选
数据范围
- 全部数据:,
- 测试点 :,
- 测试点 :
- 测试点 :
- 测试点 :
- 测试点 :,
- 测试点 :,
- 测试点 :,
- 1
信息
- ID
- 3199
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者