
2025-12-06:硬币面值还原。用go语言,给出一个从 1 开始索引的整数数组 numWays,其中 numWays[i] 表示用若干种固定面额且每种可重复使用的硬币,凑出金额 i 的方案数。所有面额都是正整数,且最大不会超过 numWays 的长度。目前具体的面额信息丢失了,你需要推断出可能导致该 numWays 的硬币面额集合。
输出应为一个按升序排列的不重复面额列表(即所有可能出现的面额);如果不存在任何能生成该 numWays 的面额组合,则返回空数组。
输入: numWays = [0,1,0,2,0,3,0,4,0,5]。
输出: [2,4,6]。
解释:
金额 | 方法数 | 解释 |
|---|---|---|
1 | 0 | 无法用硬币凑出总金额 1。 |
2 | 1 | 唯一的方法是 [2]。 |
3 | 0 | 无法用硬币凑出总金额 3。 |
4 | 2 | 可以用 [2, 2] 或 [4]。 |
5 | 0 | 无法用硬币凑出总金额 5。 |
6 | 3 | 可以用 [2, 2, 2]、[2, 4] 或 [6]。 |
7 | 0 | 无法用硬币凑出总金额 7。 |
8 | 4 | 可以用 [2, 2, 2, 2]、[2, 2, 4]、[2, 6] 或 [4, 4]。 |
9 | 0 | 无法用硬币凑出总金额 9。 |
10 | 5 | 可以用 [2, 2, 2, 2, 2]、[2, 2, 2, 4]、[2, 4, 4]、[2, 2, 6] 或 [4, 6]。 |
题目来自力扣3592。
n+1 的数组 f(其中 n 是 numWays 的长度),用于记录使用当前已推断出的硬币面额组合,凑出金额 0 到 n 的方案数。f[0] 初始化为 1,表示凑出金额 0 的方案数为 1(即不取任何硬币)。对于 i 从 1 到 n,f[i] 的初始值均为 0。i = 1 开始遍历至 i = n(对应 numWays 的索引为 i-1):numWays[i-1] 的值,即题目给定的凑出金额 i 的方案数,记为 ways。ways 与当前动态规划数组计算出的方案数 f[i]:ways == f[i]i 的方案数,无需添加新的面额。直接继续处理下一个金额 i+1。ways != f[i]ways - 1 == f[i] 是否成立:i 的硬币来弥补方案数的差距。将面额 i 加入结果集 ans,并执行下一步的完全背包更新。i 来满足给定的方案数,立即返回空数组 nil,表示不存在可行的硬币面额组合。i 后,需要更新动态规划数组 f 以反映新面额对后续金额方案数的影响。这个过程类似于解决完全背包问题。j 从 i 到 n(从小到大),执行 f[j] += f[j - i]。这表示,对于每个金额 j,新增的方案数等于使用新面额 i 后,凑出金额 j-i 的方案数(因为加上一个面额 i 的硬币即可凑出金额 j)。 通过这样的更新,f 数组能动态地记录当前所有已选面额组合下的方案数。1 到 n 且没有返回空数组,则收集到的结果集 ans 就是按升序排列的可能硬币面额集合(因为 i 是递增遍历的)。O(n^2)。i(从 1 到 n),共 n 次循环。i 到 n),内层循环的次数平均约为 n/2。O(n^2)。O(n)。f,其大小为 n+1,因此额外空间复杂度为线性 O(n)。这个算法的核心在于贪心策略和动态规划的结合。它从小到大依次考虑每个金额,通过对比给定方案数与当前计算方案数的差异,动态地推断出必须存在的硬币面额,并利用完全背包的思想更新方案数。其正确性依赖于硬币面额是正整数且不超过 n 的条件,从而保证了解的唯一性(如果存在)。
package main
import (
"fmt"
)
func findCoins(numWays []int) (ans []int) {
n := len(numWays)
f := make([]int, n+1)
f[0] = 1
for i := 1; i <= n; i++ {
ways := numWays[i-1]
if ways == f[i] {
continue
}
if ways-1 != f[i] {
returnnil
}
ans = append(ans, i)
// 现在得到了一个大小为 i 的物品,用 i 计算完全背包
for j := i; j <= n; j++ {
f[j] += f[j-i]
}
}
return
}
func main() {
numWays := []int{0, 1, 0, 2, 0, 3, 0, 4, 0, 5}
result := findCoins(numWays)
fmt.Println(result)
}

# -*-coding:utf-8-*-
from typing importList
deffindCoins(numWays: List[int]) -> List[int]:
n = len(numWays)
f = [0] * (n + 1)
f[0] = 1
ans = []
for i inrange(1, n + 1):
ways = numWays[i - 1]
if ways == f[i]:
continue
if ways - 1 != f[i]:
returnNone
ans.append(i)
# 现在得到了一个大小为 i 的物品,用 i 计算完全背包
for j inrange(i, n + 1):
f[j] += f[j - i]
return ans
defmain():
numWays = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5]
result = findCoins(numWays)
print(result)
if __name__ == "__main__":
main()
#include <iostream>
#include <vector>
usingnamespace std;
vector<int> findCoins(const vector<int>& numWays) {
int n = numWays.size();
vector<int> f(n + 1, 0);
vector<int> ans;
f[0] = 1;
for (int i = 1; i <= n; i++) {
int ways = numWays[i - 1];
if (ways == f[i]) {
continue;
}
if (ways - 1 != f[i]) {
return {}; // 返回空vector
}
ans.push_back(i);
// 现在得到了一个大小为 i 的物品,用 i 计算完全背包
for (int j = i; j <= n; j++) {
f[j] += f[j - i];
}
}
return ans;
}
int main() {
vector<int> numWays = {0, 1, 0, 2, 0, 3, 0, 4, 0, 5};
vector<int> result = findCoins(numWays);
// 输出结果
cout << "[";
for (size_t i = 0; i < result.size(); i++) {
cout << result[i];
if (i != result.size() - 1) {
cout << ", ";
}
}
cout << "]" << endl;
return0;
}

我们相信人工智能为普通人提供了一种“增强工具”,并致力于分享全方位的AI知识。在这里,您可以找到最新的AI科普文章、工具评测、提升效率的秘籍以及行业洞察。 欢迎关注“福大大架构师每日一题”,发消息可获得面试资料,让AI助力您的未来发展。