NOIP2018提高组货币系统详解:从问题分析到最优解法
2018NOIP提高组的题目货币系统是NOIP竞赛中考察动态规划和数学思维的典型题目。本文将完整解析题目要求,并通过详细注释的代码展示如何将问题转化为完全背包问题来解决。
一、问题重述
给定一个货币系统(n,a),求一个等价的货币系统(m,b),使得m尽可能小。等价的意思是两种货币系统能够表示的金额完全相同。
二、核心算法思想
三、完整代码实现(带详细注释)
#include <iostream> #include <vector> #include <algorithm> #include <cstring> using namespACe std; const int MAXN = 105; const int MAXV = 25005; int main() { int T; cin >> T; while (T--) { int n; cin >> n; vector<int> a(n); for (int i = 0; i < n; i++) { cin >> a[i]; } // 排序货币面额(关键步骤1:确保处理顺序正确) sort(a.begin(), a.end()); // dp[i]表示金额i能否被表示(关键步骤2:初始化动态规划数组) bool dp[MAXV] = {false}; dp[0] = true; // 金额0总是可以被表示 int ans = 0; int max_val = a.back(); // 最大面额(优化循环边界) // 核心算法部分(关键步骤3:完全背包思想) for (int i = 0; i < n; i++) { int val = a[i]; // 如果当前面额不能被前面的货币表示,则必须保留 if (!dp[val]) { ans++; // 完全背包更新(关键步骤4:更新可表示的金额) for (int j = val; j <= max_val; j++) { if (dp[j - val]) { dp[j] = true; } } } } cout << ans << endl; } return 0; }
四、关键步骤解析
输入处理:处理多组测试数据,适应题目要求
排序优化:将货币面值从小到大排序,确保处理顺序正确
动态规划数组:dp数组记录每个金额是否可被表示
核心算法:
遍历每个面值
如果当前面值不能被已处理的面值表示,则必须保留
使用完全背包思想更新可表示的金额
五、学习建议
原创内容 转载请注明出处