Dynamic Programming【DP】1
问题 A: Dice Combinations
题目描述
Your task is to count the number of ways to construct sum n by throwing a dice one or more times. Each throw produces an outcome between 1 and 6.
For example, if n=3, there are 4 ways:
1+1+1
1+2
2+1
3输入
The only input line has an integer n.
Constraints
1 ≤ n ≤ 106输出
Print the number of ways modulo 109+7.
样例输入
复制
3样例输出
复制
4
在计算 dp[i] 时,判断条件用 i >= j 而不是 i > j,是因为当 i == j 时,这种情况是合法且需要被计入的。
我们结合具体场景理解:
核心逻辑回顾
dp[i] 表示 “得到总和 i 的方式数”,其递推公式的思路是:
最后一次掷出的数字为 j(1≤j≤6),则之前的总和必须是 i - j。
因此,只有当 i - j ≥ 0 时,i - j 才是一个有效的 “之前的总和”(总和不能为负数),对应的情况才能被计入 dp[i]。
为什么 i >= j 是正确的?
当 i == j 时:
i - j = 0,即 “之前的总和为 0”(对应dp[0] = 1,表示 “不掷骰子” 的基础状态)。这种情况的实际意义是:直接通过一次掷出
j(此时j = i)得到总和i。
例如,i=3,j=3 时:
i >= j成立(3 >= 3),因此dp[3]会累加dp[0](值为 1)。这对应 “直接掷出 3” 这一种方式,是题目中明确要求计入的(如样例中
n=3的第 4 种方式)。
若用 i > j 会怎样?
如果判断条件是 i > j,则当 i == j 时(如 i=3, j=3)会被排除,导致:
dp[i]中缺少 “直接掷出i” 这一种方式,结果偏小。例如
i=3时,dp[3]会漏掉dp[0]的贡献,计算结果为dp[2] + dp[1] = 2 + 1 = 3,与正确答案 4 不符。
结论
i >= j 是正确的,因为它包含了 “最后一次掷出的数字恰好等于 i” 的合法情况(此时 i - j = 0,对应 dp[0] 的贡献)。而 i > j 会错误地排除这种情况,导致结果不正确。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
const int mod=1e9+7;ll n,dp[N];int main()
{ cin>>n;dp[0]=1;for(int i=1;i<=n;++i){for(int j=1;j<=6&&i-j>=0;++j){dp[i]=(dp[i]+dp[i-j])%mod;}}cout<<dp[n];return 0;
}问题 B: Minimizing Coins
题目描述
Consider a money system consisting of n coins. Each coin has a positive integer value. Your task is to produce a sum of money x using the available coins in such a way that the number of coins is minimal.
For example, if the coins are {1,5,7} and the desired sum is 11, an optimal solution is 5+5+1 which requires 3 coins.输入
The first input line has two integers n and x: the number of coins and the desired sum of money.
The second line has n distinct integers c1,c2,...,cn: the value of each coin.
Constraints
1 ≤ n ≤ 100
1 ≤ x ≤ 106
1 ≤ ci ≤ 106输出
Print one integer: the minimum number of coins. If it is not possible to produce the desired sum, print -1.
样例输入
复制
3 11 1 5 7样例输出
复制
3
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;int n,x,coin[N];
vector<int>dp(N,1e9);int main()
{cin>>n>>x;for(int i=0;i<n;++i)cin>>coin[i];sort(coin,coin+n);dp[0]=0;for(int i=1;i<=x;++i){for(int j=0;j<n&&coin[j]<=i;++j){dp[i]=min(dp[i],dp[i-coin[j]]+1);}}if(dp[x]!=1e9)cout<<dp[x];else cout<<"-1";return 0;
}问题 C: Coin Combinations I
题目描述
Consider a money system consisting of n coins. Each coin has a positive integer value. Your task is to calculate the number of distinct ways you can produce a money sum x using the available coins.
For example, if the coins are {2,3,5} and the desired sum is 9, there are 8 ways:
2+2+5
2+5+2
5+2+2
3+3+3
2+2+2+3
2+2+3+2
2+3+2+2
3+2+2+2输入
The first input line has two integers n and x: the number of coins and the desired sum of money.
The second line has n distinct integers c1,c2,...,cn: the value of each coin.
Constraints
1 ≤ n ≤ 100
1 ≤ x ≤ 106
1 ≤ ci ≤ 106输出
Print one integer: the number of ways modulo 109+7.
样例输入
复制
3 9 2 3 5样例输出
复制
8
结果重复计数的原因是没有区分硬币的使用顺序,导致相同组合因顺序不同被多次统计。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
const int mod=1e9+7;int n,x,coin[N];
vector<int>dp(N,0);int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);cin>>n>>x;for(int i=0;i<n;++i)cin>>coin[i];sort(coin,coin+n);dp[0]=1;for(int i=1;i<=x;++i){for(int j=0;j<n&&coin[j]<=i;++j){dp[i]=(dp[i]+dp[i-coin[j]])%mod;}}cout<<dp[x];return 0;
}问题 D: Coin Combinations II
如上题,附加要求方案不能重复。
遍历顺序调整
先遍历硬币(for(int j=0;j<n;++j)),再遍历金额(for(int i=c;i<=x;++i))。这种方式确保:- 每种硬币只能被 “按顺序” 使用,例如先用完所有 2 元,再用 3 元,最后用 5 元。
- 避免了 “2+3” 和 “3+2” 被算作两种不同方案的问题。
金额遍历范围
对于硬币c,只从i=c开始遍历,因为只有金额 >=c时才能使用该硬币,提高效率。
for(int i=0;i<n;++i){for(int j=1;j<=x;++j){if(j>=coin[i])dp[j]=(dp[j]+dp[j-coin[i]])%mod;}}问题 E: Removing Digits
题目描述
You are given an integer n. On each step, you may subtract one of the digits from the number.
How many steps are required to make the number equal to 0?输入
The only input line has an integer n.
Constraints
1 ≤ n ≤ 106输出
Print one integer: the minimum number of steps.
样例输入
复制
27样例输出
复制
5提示
An optimal solution is 27 → 20 → 18 → 10 → 9 → 0.
对于每个数字 i,枚举其所有数位 d(d 是 i 的每一位上的数字,且 d > 0),则:
dp[i] = min(dp[i], dp[i - d] + 1)
(含义:从 i 减去 d 后得到 i-d,步骤数比 dp[i-d] 多 1)
dp[27]的计算需要考虑其数位2和7:若减去
7,则dp[27] = dp[20] + 1若减去
2,则dp[27] = dp[25] + 1
最终通过递推,
dp[27]会取两种选择中的最小值,得到最少步骤数。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;int n;
vector<int>dp(N,1e9);int main()
{cin>>n;dp[0]=0;for(int i=1;i<=n;++i){int temp=i;while(temp){int x=temp%10;if(i>=x&&x)dp[i]=min(dp[i],dp[i-x]+1);temp/=10;}}cout<<dp[n];return 0;
}