1. 问题分析与算法选型
这道金币分割问题看似简单,但蕴含着典型的动态规划思想。我们需要解决两个核心问题:一是找到两堆金币的最小价值差,二是计算达到这个最小差的分割方法数。
1.1 问题本质剖析
题目可以转化为经典的"子集和"问题变种:给定一组数字,将其分成两个子集,使得两个子集的和尽可能接近。这与背包问题有异曲同工之妙:
- 背包容量:总价值的一半(sum/2)
- 物品:每枚金币
- 物品价值:金币面值
- 目标:尽可能装满背包
1.2 为什么选择动态规划
暴力枚举所有可能的子集显然不可行,时间复杂度高达O(2^N),当N=250时计算量完全不可接受。动态规划的优势在于:
- 时间复杂度优化:O(Nsum)的时间复杂度对于N≤250和v_i≤2000是可接受的(sum最大为2502000=500,000)
- 空间优化:使用滚动数组可以将空间复杂度控制在O(sum)
- 可扩展性:同样的框架可以同时解决最小差值和方法数两个问题
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 算法实现详解
2.1 预处理与初始化
首先我们需要计算所有金币的总价值:
cpp复制int sum = 0;
for(int i=1;i<=n;i++) {
a[i] = read();
sum += a[i];
}
int half = sum / 2; // 目标背包容量
这里half表示理想情况下每堆金币能达到的最大值(不超过总价值的一半)。
2.2 求解最大可达价值
我们使用经典的0-1背包思路来求解:
cpp复制vector<int> f(half+1, 0);
for(int i=1;i<=n;i++) {
for(int j=half;j>=a[i];j--) {
f[j] = max(f[j], f[j-a[i]]+a[i]);
}
}
f[j]表示容量为j的背包能装下的最大价值。通过逆序更新确保每个金币只被使用一次。
2.3 计算最小差值
得到f[half]后,最小差值就是:
cpp复制int min_diff = sum - 2 * f[half];
这个公式的推导很简单:一堆价值为f[half],另一堆就是sum - f[ha
