寒假每日一题货币系统(个人练习)详细题解+推导证明(第十七天)
Posted 我是管小亮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了寒假每日一题货币系统(个人练习)详细题解+推导证明(第十七天)相关的知识,希望对你有一定的参考价值。
文章目录
前言
今天是大年初三,但是感觉年已经过去了,哈哈。
今天的题我还忘记发了,
题目
给定 V 种货币(单位:元),每种货币使用的次数不限。
不同种类的货币,面值可能是相同的。
现在,要你用这 V 种货币凑出 N 元钱,请问共有多少种不同的凑法。
输入格式
- 第一行包含两个整数 V 和 N。
- 接下来的若干行,将一共输出 V 个整数,每个整数表示一种货币的面值。
输出格式
- 输出一个整数,表示所求总方案数。
数据范围
- 1 ≤ V ≤ 25 , 1≤V≤25, 1≤V≤25,
- 1 ≤ N ≤ 10000 1≤N≤10000 1≤N≤10000
- 答案保证在long long范围内。
输入样例:
3 10
1 2 5
输出样例:
10
详细题解
写法1 O ( n m ) O(nm) O(nm)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 30, M = 10005;
int n, m;
LL f[N][M];
int main()
cin >> n >> m;
f[0][0] = 1;
for (int i = 1; i <= n; ++ i )
int v;
cin >> v;
for (int j = 0; j <= m; ++ j )
f[i][j] = f[i-1][j];
if (j >= v) f[i][j] += f[i][j-v];
cout << f[n][m] << endl;
return 0;
毫无疑问,这是一个动态规划的题目。
除此之外,这还是个经典的完全背包问题。
最后提交,AC😁
推导证明
首先需要划分两个情况:
- 一个是状态表示;
- 一个是状态计算;
每次划分的不同是最后一个位置能否整除v:
- f[i][j]等于每个位置减去n倍的v;
- 同理,f[i][j-v]等于除了f[i-1][j]的f[i][j]。
写法2 O ( n m ) O(nm) O(nm)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 30, M = 10005;
int n, m;
LL f[M];
int main()
cin >> n >> m;
f[0] = 1;
for (int i = 1; i <= n; ++ i )
int v;
cin >> v;
for (int j = v; j <= m; ++ j )
f[j] += f[j-v];
cout << f[m] << endl;
return 0;
最后提交,AC😁
推导证明
优化的方案是将二维数组变成一维数组,其实在意义上很难理解优化,所以只是一种等价替换。
- 首先去掉二维数组的第一个维度;
- 然后判断是否等价;
- 最后提交AC;
需要注意的是,由于是完全背包问题,所以每个硬币可以被无限次使用,即等价于:
for (int j = v; j <= m; ++ j )
f[j] += f[j-v];
如果是01背包问题的话,就需要调整j的数据范围,
- 从大到小循环 f[j-v] 等价于 f[i-1][j-v],
- 从小到大循环等价于 f[i][j-v]。
由于状态转移方程是 f[j] += f[j-v];
,所以涉及到 f[j] 和 f[j-v] 的问题,后者一定前者,所以需要注意大小关系。
举一反三
同样的动态规划问题:【寒假每日一题】数字三角形(个人练习)详细题解+推导证明(第二天)
总结
继续努力,坚持更新,17th打卡。
以上是关于寒假每日一题货币系统(个人练习)详细题解+推导证明(第十七天)的主要内容,如果未能解决你的问题,请参考以下文章
寒假每日一题货仓选址(个人练习)详细题解+推导证明(第一天)
寒假每日一题找硬币(个人练习)详细题解+推导证明(第十二天)