惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Help Net Security
Apple Machine Learning Research
Apple Machine Learning Research
A
About on SuperTechFans
MongoDB | Blog
MongoDB | Blog
Y
Y Combinator Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Security Latest
Security Latest
Project Zero
Project Zero
A
Arctic Wolf
L
LINUX DO - 热门话题
Microsoft Azure Blog
Microsoft Azure Blog
P
Palo Alto Networks Blog
Know Your Adversary
Know Your Adversary
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Cloudbric
Cloudbric
大猫的无限游戏
大猫的无限游戏
Google DeepMind News
Google DeepMind News
G
Google Developers Blog
Stack Overflow Blog
Stack Overflow Blog
T
Threatpost
T
The Exploit Database - CXSecurity.com
T
Tailwind CSS Blog
PCI Perspectives
PCI Perspectives
WordPress大学
WordPress大学
T
Tor Project blog
阮一峰的网络日志
阮一峰的网络日志
The Hacker News
The Hacker News
V
Visual Studio Blog
M
MIT News - Artificial intelligence
月光博客
月光博客
D
DataBreaches.Net
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Simon Willison's Weblog
Simon Willison's Weblog
Attack and Defense Labs
Attack and Defense Labs
The Register - Security
The Register - Security
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
MyScale Blog
MyScale Blog
N
Netflix TechBlog - Medium
S
Security Affairs
T
The Blog of Author Tim Ferriss
P
Proofpoint News Feed
Spread Privacy
Spread Privacy
AI
AI
S
Schneier on Security
L
LangChain Blog
C
Cybersecurity and Infrastructure Security Agency CISA
博客园 - 叶小钗
量子位
H
Heimdal Security Blog
J
Java Code Geeks

潮汐朝夕

Ad-Hoc问题:在字符串中删改字符 leetcode第一题,两数之和 频繁查询子串是否回文:区间DP 频繁查询子串是否回文:Manacher预处理后$O(1)$响应 状态转移方程描述最优子结构 | 优化状态表示 | 单调队列优化DP 树形 DP | 剪枝优化 | 移位操作 滑动窗口 | 按位单独处理 | 字符计数 通过模拟解决算法问题:导论 Python的生成器对象与 filter 对象 按位单独处理的技巧 滑动窗口 | 满足条件的最短的子串 滑动窗口字符计数的优化:增加维护聚合信息应对高频查询 滑动窗口 | 满足条件的子串数目 最少硬币组合问题2:贪心算法【附算法导论习题答案下载】 力扣2274-不含特殊楼层的最大连续楼层数 带余除法 | 算法式证明 | 进制转换 2025年的内容与栏目建设 概率方法 | Cr不等式 | r阶绝对矩 Weierstrass不等式的另一边 | Bernoulli不等式的推广
最少硬币组合问题1:完全背包
Cheng Zhaoxi · 2025-01-05 · via 潮汐朝夕

字数统计: 934字   |   阅读时长: 3分

摘要: 最少硬币组合问题,对任意面额集的动态规划算法


【对算法,数学,计算机感兴趣的同学,欢迎关注我哈,阅读更多原创文章】
我的网站:潮汐朝夕的生活实验室
我的公众号:算法题刷刷
我的知乎:潮汐朝夕
我的github:FennelDumplings
我的leetcode:FennelDumplings

我们此前详细讲解过背包问题,题目的分类汇总参考 01背包和完全背包题目汇总。其中硬币组合是完全背包的一个经典问题。在完全背包中,每个物品都是不限个数的,我们要做的是在背包容量范围内,拿走的总价值尽可能多。

而硬币组合问题中,每种面值的硬币也是不限个数,我们要做的是拿走的面值总和刚好是给定值,使得硬币的个数最少。从背包的角度看就是,硬币的面值相当于体积,每个硬币的价值都是 1,取走的物品刚好填满背包容量,拿走的总价值尽可能少。

给定一系列可选的面值,以及一个给定的数额,最少用多少枚硬币可以拼凑出给定的数额。当面值集合没有其它条件时,可以用类似完全背包的动态规划解决,本文我们看这个问题。

最少硬币组合问题,动态规划是任意面额集合都成立的算法,当面额集满足某些条件时,可以用贪心算法得到最优解。后续我们讨论一下这个问题,感兴趣的朋友可以参考《算法导论》第三版的习题 16-1。

题目

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

提示:

1
2
3
1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 1e4

示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1

示例 2:
输入:coins = [2], amount = 3
输出:-1

示例 3:
输入:coins = [1], amount = 0
输出:0

题解

算法:完全背包、动态规划

面额集为 coins,其面额记为 $c[i], i=0,1,2,\cdot,n-1$,不妨设 $0 < c[0] < c[1] < \cdots < c[n-1]$。

当只使用面值 $c[0],\cdots,c[i]$ 的硬币时,可以凑出金额 $j$ 的最少硬币个数记为 $dp[i][j]$,这样定义的话,边界情况为 $dp[i][0] = 0, i=0,1,\cdots,n-1$。

在 $dp[i][j]$ 这个状态的时候,有两种可能的决策,即是否使用 $c[i]$ 面额,若使用,则问题变为 $dp[i][j - c[i]]$,若不使用,则问题变为 $dp[i-1][j]$,选其最小的即可,于是状态转移方程如下:

由于当推导到 $dp[i][j]$ 时,$dp[i+1][\cdots]$ 的所有状态已经计算完成了,而且 $i$ 这一维的推导方向是从大到小地推的,因此 $i$ 这一维可以通过滚动数组优化。

代码 (C++)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
using ll = long long;
if(coins.empty()) return 0;

int N = coins.size();
vector<int> dp(amount + 1, INT_MAX);
dp[0] = 0;
for(int i = 0; i < N; ++i)
for(int j = coins[i]; j <= amount; ++j)
dp[j] = min((ll)dp[j], (ll)dp[j - coins[i]] + 1);
if(dp[amount] == INT_MAX) return -1;
return dp[amount];
}
};