/*
* 有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;
}