Description
小明手上有 N!!!张纸币(他的钱很奇怪,可以是任意的正整数), 现在他想知道 用这N种纸币可以组合出多少种不同的总额出来。
Format
Input
第一行一个N.
第二行N个正整数,总和不超过10^8
N<=20
Output
如题
Samples
输入数据 1
5
1 1 2 3 4
输出数据 1
11
题解
有两种方法:
第一种:递归
我估计这种方法是每个读完题的人第一时间想到的方法了呵呵。
我们可以给每张钱币设两种状态:0是不取,1是取。然后我们开始递归:
#include<bits/stdc++.h>
using namespace std;
int n,a[21];
bool b[100000001];
void dfs(int id,int sum) //id是我当前是第几张钱币,sum是当前钱的总和
{
if(id==n+1) //已经枚举完n张钱币了
{
b[sum]=1; //标记一下当前总和
return;
}
for(int i=0;i<=1;i++)
dfs(id+1,sum+a[id]*i);
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
dfs(1,0);
int ans=0;
for(int i=1;i<=100000001;i++)
{
if(b[i])ans++;
}
cout<<ans;
return 0;
}
第二种:数学方法
我们可以发现:既然0是不取,1是取。这不就是二进制吗!
比如n是4,这4张钱币就有0000~1111这几种状态。1111是十进制的15,是2^4-1。
我们可以枚举i从1到2^n-1,每次将i转成二进制并放到数组里,将每一位乘a[i],最后记录总和sum就行了。
#include<bits/stdc++.h>
using namespace std;
int n,t,a[150000001];
bool b[150000001];
int c[150000001],u=0;
int main()
{
cin>>n;
t=(1<<n)-1; //计算2^n-1
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
int cnt=0;
for(int i=1;i<=t;i++)
{
int t1=i;
u=0;
int j=i;
while(j>0)
{
c[++u]=j%2;
j/=2;
}
int sum=0;
for(int j=u;j>=1;j--)
{
sum+=c[j]*a[j]; //计算总和sum
}
if(!b[sum]) //标记一下
{
b[sum]=1;
cnt++;
}
}
cout<<cnt;
return 0;
}