Coins

/*
* 有n种面额的硬币。第i个硬币的面额是v[i],个数为c[i]。
* 问最多能搭配出多少种不超过m的金额。
* 1<=n<=100,1<=m<=100000,1<=v[i]<=100000,1<=c[i]<=1000
* 例:
*	n=3,m=10,v[n]={1,2,4},c[n]={2,1,1}。
*	答案是8。
* 设f[i][j]表示考虑到第i种硬币,金额为j的最大搭配方案。1<=i<=n,1<=j<=m
* 1、i=1时,即考虑第一种硬币,要求配满金额为j。
*	 这里需要依次枚举第一种硬币的数量,记为k。0<=k<=c[1]。满足题意的应该是k*v[1]=j。
*	 如果在[0,c[i]]这个区间枚举完了发现依然找不到满足题意的解,那么就有f[1][j]=0。
* 2、i>1时,仍然需要依次枚举第一种硬币的数量,记为k。0<=k<=c[i]。 
*	 考虑第i次时的金额为j,那么考虑第i-1次时的金额就应该为j-k*c[i]。
*	 完整的递推方程为f[i][j]=sum(f[i-1][j-k*v[i]]),0<=k<=c[i],1<i<=n。(前面所有方案数量的总和)
*	 观察方程j=k*v[i]时,将会有一种情况产生,因此可以令f[i][0]=1,f[0][j](1<=j<=m)=0
*	 这样递归方程f[i][j]=sum(f[i-1][j-k*v[i]]),0<=k<=c[i],1<=i<=n。
* 最终结果应该看f[n][j]中的有多少结果不为0。再将不为负的全部输出就行了。
* 观察递推方程,发现还可以进一步优化空间复杂度,j的遍历可以采取倒序,递推方程化简:
* f[j]=sum(f[j-k*v[i]]),0<=k<=c[i],1<=i<=n。
* 最终结果应该看f[j]中的有多少结果不为0。再将不为负的全部输出就行了。
*/
#include<iostream>
#include<vector>
using namespace std;
const vector<int>v = { 1,2,4 };
const vector<int>c = { 2,1,1 };
const int n = (int)v.size();
const int m = 10;
int main()
{
	int* f = new int[m + 1]{ 1 };
	int sum = 0;
	for (int i = 1; i <= n; i++)
	{
		for (int j = m; j >= 1; j--)
		{
			for (int k = 0; k <= c[i - 1]; k++)
			{
				if (j - k * v[i - 1] < 0)//防止越界
					break;
				f[j] += f[j - k * v[i - 1]];
			}
			if (i == n && f[j] != 0)
				sum++;
		}
	}

	cout << "一共有" << sum << "种金额不超过" << m << "的情况。"<<endl;
	for (int i = 1; i <= m; i++)
		if (f[i] != 0)
			cout << i << endl;
	delete[] f;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值