题解:洛谷 P1474 Cow Cash
【题目来源】
洛谷:P1474 [USACO2.3] Money System / [USACO07OCT] Cow Cash G - 洛谷
【题目描述】
母牛们不但创建了它们自己的政党,而且选择了建立了自己的货币系统。由于它们特殊的思考方式,它们对货币的数值感到好奇。
传统地,一个货币系统是由 \(1,5,10,20,25,50,100\) 的单位面值组成的。
母牛们想知道有多少种不同的方案来用货币系统中的货币来构造一个确定的面值。
举例来说,使用一个由 \(1,2,5,10\) 的单位面值组成的货币系统产生 \(18\) 面值的一些可能的方法是:\(18 \times 1\),\(9 \times 2\),\(8 \times 2+2 \times 1\),\(3 \times 5+2+1\),等等。
写一个程序来计算有多少种方法用给定的货币系统来构造一定数量的面值。保证总数在 \(64\) 位带符号整数的范围内。
【输入】
第一行两个整数,代表货币系统中货币的种类数目 \(V\)(\(1 \leq V \leq 25\))和要构造的面值 \(N\)(\(1 \leq N \leq 10,000\))。
第二行 \(V\) 个整数,代表所有货币的单位面值。
【输出】
仅一行一个整数,代表方案数。
【输入样例】
3 10
1 2 5
【输出样例】
10
【核心思想】
-
问题分析:给定 \(V\) 种货币面值和要构造的面值 \(N\),计算有多少种不同的方案可以用这些货币构造出面值 \(N\)。每种货币可以无限使用,且方案不考虑顺序(组合问题)。这是一个计数型完全背包问题,关键在于用DP统计方案数而非求最值。
-
算法选择:
- 计数型完全背包:
dp[j]表示组成金额 \(j\) 的方案数 - 状态转移:
dp[j] += dp[j - a[i]],将物品 \(i\) 加入金额 \(j - a[i]\) 的所有方案中 - 初始化:
dp[0] = 1(组成金额0的方案数为1,即不选任何物品)
- 计数型完全背包:
-
关键步骤:
- 初始化:
dp[0] = 1,其余为0 - 外层遍历每种货币 \(i\)(\(1 \leq i \leq V\))
- 内层从小到大遍历金额 \(j\)(\(a[i] \leq j \leq N\)),完全背包特性保证物品可重复选取
- 状态转移:
dp[j] += dp[j - a[i]],累加使用当前货币的方案数 - 答案获取:
dp[N]即组成面值 \(N\) 的方案总数
- 初始化:
-
时间/空间复杂度:
- 时间复杂度:\(O(V \times N)\),外层 \(V\) 种货币,内层 \(N\) 个金额
- 空间复杂度:\(O(N)\),一维DP数组(注意使用
long long,方案数可能很大)
-
计数型完全背包的核心思想:
- 与最值型区别:计数型是累加方案数而非取最值
- 遍历顺序:外层物品内层容量,保证每种组合只被计算一次(组合而非排列)
- 正向遍历:完全背包使用正向遍历,允许物品重复选取
- 初始化技巧:
dp[0] = 1表示空集也是一种方案 - 适用于"无限物品求组成方案数"类问题
【算法标签】
普及 #完全背包
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 30, M = 10005; // 定义最大物品数N和最大金额M
int n, m; // 物品数量n,目标金额m
int a[N]; // 存储每个物品的金额
int dp[M]; // 动态规划数组,dp[j]表示金额j的组成方案数
signed main() // 主函数
{
cin >> n >> m; // 输入物品数量和目标金额
for (int i=1; i<=n; i++) // 遍历所有物品
{
cin >> a[i]; // 输入第i个物品的金额
}
dp[0] = 1; // 初始化:金额为0的方案数为1(不选任何物品)
for (int i=1; i<=n; i++) // 遍历每个物品
{
for (int j=a[i]; j<=m; j++) // 遍历所有可能的目标金额
{
dp[j] += dp[j-a[i]]; // 状态转移方程:将当前物品加入金额j-a[i]的方案中
}
}
cout << dp[m]; // 输出组成目标金额m的方案数
return 0; // 程序正常结束
}
【运行结果】
3 10
1 2 5
10
浙公网安备 33010602011771号