题目
有1分,2分,5分,10分四种硬币,每种硬币数量无限,给定n分钱(n<100000),有多少中组合可以组成n分钱?
输入描述
输入整数n.(1<=n<=100000)
输出描述
输出组合数,答案对1e9+7取模。
这是完全背包问题和背包方案数量问题的组合,这两个问题是非常经典的问题,感兴趣的可以了解一下(博主也会尽快写出来的)。掌握了常见的背包问题模型,对动态规划的理解会更进一步。
动态规划方法
动态规划做题步骤:
1.明确dp[i]表示什么
2.根据dp[i]和dp[i-1]的关系得出状态转移方程
3.确定初始条件
使用dp[i][j]表示前i中面值的硬币构成面值为j的方案的数量,表示第i种硬币的面值。可以推导出状态转移方程:
dp[i][j]=dp[i-1][j-0*]+dp[i-1][j-1*
]+...+dp[i-1][j-k*
] (k为满足j-k*
>=0的最大整数)
优化:
dp[i][j]=dp[i-1][j]+dp[i-1][j-1*]+...+dp[i-1][j-k*
]
用j-替换j得到:dp[i][j-
]=dp[i-1][j-1*
]+...+dp[i-1][j-k*
]
化简得到:dp[i][j]=dp[i-1][j]+dp[i][j-]
现在使用的是二维数组,但是dp[i][j]=dp[i-1][j]+dp[i][j-],更新dp只需要用到第i行和i-1行,所以可以用滚动数组来进行优化,用一个一维数组DP[n]表示在计算第i轮之前DP数组中存放着i-1轮的答案。
时间复杂度:O(N)
空间复杂度:O(N)
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
int solution(int n){
int result;
// TODO:
int mod=1e9+7;
int coins[4]={10,5,2,1};
vector<int> dp(n+1);
dp[0]=1;
for(int i=0;i<4;++i){
int coin=coins[i];
for(int j=coin;j<=n;++j){
dp[j]=(dp[j]+dp[j-coin])%mod;
}
}
result=dp[n];
return result;
}
int main() {
int n;
std::cin>>n;
int result = solution(n);
std::cout<<result<<std::endl;
return 0;
}