拼凑钱币


拼凑钱币

给你六种面额 1、5、10、20、50、100 元的纸币,假设每种币值的数量都足够多,编写程序求组成N元(N为0~10000的非负整数)的不同组合的个数。

输入描述:
输入包括一个整数n(1 ≤ n ≤ 10000)


输出描述:
输出一个整数,表示不同的组合方案数
示例1

输入

1

输出

1
给定一个数值sum,假设我们有m种不同类型的硬币{V1, V2, ..., Vm},如果要组合成sum,则


                          sum = x1 * V1 + x2 * V2 + ... + xm * Vm 


求所有可能的组合数,就是求满足前面等值的系数{x1, x2, ..., xm}的所有可能个数。


我们希望用m种硬币构成sum,根据最后一个硬币Vm的系数的取值为无非有这么几种情况,xm分别取{0, 1, 2, ..., sum/Vm},换句话说,上面分析中的等式和下面的几个等式的联合是等价的。


sum = x1 * V1 + x2 * V2 + ... + 0 * Vm


sum = x1 * V1 + x2 * V2 + ... + 1 * Vm


sum = x1 * V1 + x2 * V2 + ... + 2 * Vm


...


sum = x1 * V1 + x2 * V2 + ... + K * Vm  


  其中K是该xm能取的最大数值K = sum / Vm。可是这又有什么用呢?不要急,我们先进行如下变量的定义:


dp[i][sum] = 用前i种硬币构成sum 的所有组合数。



  那么题目的问题实际上就是求dp[m][sum],即用前m种硬币(所有硬币)构成sum的所有组合数。在上面的联合等式中:当xn=0时,有多少种组合呢? 实际上就是前i-1种硬币组合sum,有dp[i-1][sum]种! xn = 1 时呢,有多少种组合? 实际上是用前i-1种硬币组合成(sum - Vm)的组合数,有dp[i-1][sum -Vm]种; xn =2呢, dp[i-1][sum - 2 * Vm]种,等等。所有的这些情况加起来就是我们的dp[i][sum]。所以:


dp[i][sum] = dp[i-1][sum - 0*Vm] + dp[i-1][sum - 1*Vm]


+ dp[i-1][sum - 2*Vm] + ... + dp[i-1][sum - K*Vm]; 其中K = sum / Vm


归纳成数学表达式即为:



代码:

#include<iostream>
#include<vector>
using namespace std;
const int tab[6] = { 1,5,10,20,50,100 };
int main() {
	long long cnt = 0;
	long long n;
	while (cin >> n) {
		vector<vector<long long> > dp(6, vector<long long>(n + 1, 0));
		for (int i = 0; i<n + 1; i++)
			dp[0][i] = 1;
		for (int i = 1; i<6; i++) {
			for (int sum = 1; sum <= n; sum++) {
				for (int k = sum / tab[i]; k >= 0; k--) {
					dp[i][sum] = dp[i][sum] + dp[i - 1][sum - k*tab[i]];
				}
			}
		}
		cout << dp[5][n] << endl;
	}
}


其实:dp[i][sum]=dp[i-1][sum]+dp[i][sum-Vm],进一步可以简化,用一维数组代替二维矩阵



代码:

#include<iostream>
#include<vector>
using namespace std;
const int tab[6]={1,5,10,20,50,100};
int main(){
    long long int cnt=0;
    long long int n;
    while(cin>>n){
        vector<long long int> dp(n+1,1);
        for(int i=1;i<6;i++){
            for(int sum=tab[i];sum<=n;sum++){
                dp[sum]=dp[sum]+dp[sum-tab[i]];
            }
        }
        cout<<dp[n]<<endl;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值