Description
小明手上有 N!!!张纸币(他的钱很奇怪,可以是任意的正整数), 现在他想知道 用这N种纸币可以组合出多少种不同的总额出来。
Format
Input
第一行一个N.
第二行N个正整数,总和不超过10^8
N<=20
Output
如题
Samples
输入数据 1
5
1 1 2 3 4
输出数据 1
11
思路1:递归
用递归来写,每次枚举当前第s个数选或不选最后枚举完n个纸币将纸币的累加结果的vis标记一下,最后枚举1~10000000,求出有多少个vis被标记了即可。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[100001],ans,n;
bool vis[100000001];
void f(int s,int p)//s:目前枚举了多少纸币 p:目前纸币总和
{
if(s == n + 1)//代表枚举完了n张纸币
{
vis[p] = 1;//因为目前纸币总和被凑出来了
return ;
}
for(int i = 0;i < 2;i++) f(s + 1,p + i * a[s]);
}
signed main()
{
cin>>n;
for(int i = 1;i <= n;i++) cin>>a[i];
f(1,0);
for(int i = 1;i <= 100000000;i++)
if(vis[i] == 1)
ans++;
cout<<ans;
return 0;
}
思路2:数学方法(二进制)
可以发现,题目中每个钱币可以选也可以不选,所以这不很像二进制吗???
因为总共有n个纸币,所以要有n种状态,n个钱币进行组合的最大数也就是二进制中的{1 1……1}n个1,也就是十进制的2^n-1。
然后我们可以从1~2^n-1进行枚举,然后转化成二进制,最后用个变量m储存二进制中的每一位*a[i]的累加和,再判断vis[m]曾经是否被凑出过,没有就vis[m]=1,ans++,循环结束后输出ans即可。
#include<bits/stdc++.h>
using namespace std;
int n,pw,a[100000001],er[150000001],ans;
bool vis[150000001];
int main()
{
cin>>n;
for(int i = 1;i <= n;i++) cin>>a[i];
pw = (1<<n) - 1;//计算2^n-1
for(int i = 1;i <= pw;i++)
{
int s = 0,t = i;
while(t)
{
s++;//s是该二进制的位数
er[s] = t % 2;//er储存t的二进制
t /= 2;
}//在这里没有翻转er数组的原因是等下从后往前枚举就好了
int m = 0;
for(int i = s;i > 0;i--) m += er[i] * a[i];//计算m
if(vis[m] == 0)
{
vis[m] = 1;
ans++;
}
}
cout<<ans;
return 0;
}