
本文介绍一个递归算法,用于将给定总金额 price 恰好拆分为 pcs 张(或枚)预设面额的组合;若无法满足数量与总和双重要求,则返回空数组。
本文介绍一个递归算法,用于将给定总金额 price 恰好拆分为 pcs 张(或枚)预设面额的组合;若无法满足数量与总和双重要求,则返回空数组。
在实际业务场景中(如现金找零、券码发放、红包拆分等),常需将一个整数金额严格拆解为固定张数的若干预设面额之和。本问题的关键约束有二:
- 输出数组长度必须 严格等于
pcs; - 数组元素之和必须 严格等于
price; - 所有元素必须来自给定面额集合
denominations = [100000, 50000, 20000, 10000, 5000, 2000, 1000]; - 同一面额可重复使用(如
40000, 4→[10000, 10000, 10000, 10000]); - 若无解,必须返回空数组
[]。
该问题本质是带数量约束的子集和变体(bounded coin change),无法用贪心法可靠求解(例如 10000, 4 中贪心会先选 5000+5000,但剩余 0 无法凑出另 2 张,而正确解为 [5000, 2000, 2000, 1000])。因此我们采用回溯式递归搜索,兼顾“选当前面额”与“跳过当前面额”两条路径:
const denominations = [100000, 50000, 20000, 10000, 5000, 2000, 1000];
const findDenominations = (price, pcs, denoms, start = 0) => {
// ✅ 成功终止:金额与张数均耗尽
if (price === 0 && pcs === 0) return [];
// ❌ 失败终止:任一约束越界,或面额用尽
if (price = denoms.length) return null;
// ? 路径一:选用当前面额(允许重复使用 → start 不递增)
const withCurrent = findDenominations(
price - denoms[start],
pcs - 1,
denoms,
start
);
if (withCurrent !== null) {
return [denoms[start], ...withCurrent];
}
// ? 路径二:跳过当前面额(尝试更小面额 → start + 1)
return findDenominations(price, pcs, denoms, start + 1);
};
const distributeDenominations = (price, pcs) =>
findDenominations(price, pcs, denominations) ?? [];
// ✅ 测试用例验证
console.log(distributeDenominations(40000, 4)); // [10000, 10000, 10000, 10000]
console.log(distributeDenominations(10000, 4)); // [5000, 2000, 2000, 1000]
console.log(distributeDenominations(40000, 2)); // [20000, 20000]
console.log(distributeDenominations(50000, 2)); // []
⚠️ 注意事项:
Alibabacloud Sdk Client Initialization For Java下载在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 该算法时间复杂度最坏为 O(D^P)(D 为面额数,P 为张数),对大数值或高张数可能较慢;生产环境建议增加缓存(记忆化)或设置递归深度限制;
- 面额数组必须降序排列(已满足),确保优先尝试大面额,提升剪枝效率;
- 返回结果不保证唯一性(如
40000, 4也可返回[20000, 10000, 5000, 5000]),但始终满足约束;若需最优解(如最少不同面额、最大面额优先等),需扩展排序或剪枝策略;- 空数组
[]是唯一合法失败标识,不可返回null或undefined,便于调用方统一判断。
此方案逻辑清晰、边界明确,可直接集成至财务系统或前端交互模块,为金额拆分类需求提供健壮可靠的计算基础。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











