题目描述
母牛们不但创建了他们自己的政府而且选择了建立了自己的货币系统。,他们对货币的数值感到好奇。传统地,一个货币系统是由1,5,10,20 或 25,50, 和 100的单位面值组成的。
母牛想知道有多少种不同的方法来用货币系统中的货币来构造一个确定的数值。举例来说, 使用一个货币系统 {1,2,5,10,...}产生 18单位面值的一些可能的方法是:18x1, 9x2, 8x2+2x1, 3x5+2+1,等等其它。写一个程序来计算有多少种方法用给定的货币系统来构造一定数量的面值。保证总数将会适合long long (C/C++) 和 Int64 (Free Pascal)。
输入
输入包含多组测试数据
货币系统中货币的种类数目是 V 。 (1<= V<=25)
要构造的数量钱是 N 。 (1<= N<=10,000)
第 1 行: 二整数, V 和 N
第 2 ..V+1行:可用的货币 V 个整数 (每行一个,每行没有其它的数)。
输出
单独的一行包含那个可能的构造的方案数
样例输入
3 10
1 2 5
样例输出
10
该题属于完全背包的范畴,但是注意和原始的背包问题不同,这里是求方案数。令dp[i][v]表示用前i种硬币能表示v数量货币的方法数,则对第i种硬币也有选和不选的策略,而总的方案数为二者之和,因此可得状态转移方程:
dp[i][v]=dp[i-1][v]+dp[i][v-w[i]] (1<=i<=n,w[i]<=v<=V)
边界dp[1][v]=1(其余dp初始皆为0),同样,该方程也可以直接去掉i维,但保证v从w[i]开始顺序枚举。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int w[28];
long long dp[10008];
int main(){
int m,n,i,v;
while(cin>>n>>m){
memset(dp,0,sizeof(dp));
dp[0]=1;
for(i=0;i<n;i++){
cin>>w[i];
}
for(i=0;i<n;i++){
for(v=w[i];v<=m;v++){
dp[v]=dp[v]+dp[v-w[i]];
}
}
cout<<dp[m]<<endl;
}
}
dfs解法:
#include <iostream>
using namespace std;
int total = 0;
int v[10001];
void dfs(int a[],int n,int m,int level)
{
int sum=0;
for(int j=0;j<level;j++){
sum+=v[j];
}
if(sum>=m){
if(sum==m)
total++;
return;
}
for(int i=0;i<n;i++){
v[level]=a[i];
if(!level || v[level-1]<=v[level]) //保存升序,剪枝
dfs(a,n,m,level+1);
}
}
int main()
{
int a[28]={1,2,5}, n=3, m=10;
cin >> n >> m;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
dfs(a,n,m,0);
cout<<total<<endl;
}