1. 问题概述与理解
洛谷P2840纸币问题2是一个经典的动态规划问题,题目要求我们计算使用n种不同面额的无限数量纸币凑出金额w的所有可能方式的数量,其中不同的支付顺序被视为不同的方案。例如,使用1元和2元纸币凑3元时,1+2和2+1被视为两种不同的方案。
这个问题的核心在于理解"考虑支付顺序"这一条件对动态规划解法的影响。在传统的硬币找零问题中,我们通常不考虑硬币的使用顺序,而这里需要将顺序纳入考虑范围,这使得问题的解法与常规的完全背包问题有所不同。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 动态规划思路解析
2.1 状态定义
我们定义dp[k]为凑出金额k的方案数。初始条件是dp[0] = 1,表示凑出金额0有一种方案(即不使用任何纸币)。
2.2 状态转移方程
由于需要考虑支付顺序,我们需要重新思考状态转移的方式。对于金额k,我们可以考虑所有可能的最后一张纸币:
dp[k] = Σ dp[k - a[i]] (对于所有i满足k - a[i] ≥ 0)
这意味着凑出金额k的方案数等于所有凑出金额k-a[i]的方案数的总和,其中a[i]是最后一张使用的纸币面额。
2.3 与不考虑顺序的对比
在不考虑支付顺序的情况下,我们通常会使用以下方法:
- 初始化dp[0] = 1
- 对于每种纸币,从小到大更新金额
这种方法确保了我们不会重复计算不同顺序的组合。而在考虑顺序的问题中,我们需要反过来思考:对于每个金额,考虑所有可能的最后一步。
3. 算法实现细节
3.1 代码实现解析
以下是完整的C++实现代码,包含详细注释:
cpp复制#include <bits/stdc++.h>
#define int long long // 使用长整型防止溢出
#define endl '\n'
using namespace std;
#define Max 10005
#define mod 1000000007
int T;
int n, a[Max], w, dp[Max]; // dp[i]:凑出金额i的方案数
void solve() {
for (int i = 1; i <= w; i++) { // 遍历所有金额
for (int j = 1; j <= n; j++) {
