Description
给出n个数a[i],现在可以在其中任意选出若干个数,问有多少种选择方案,使得这几个数可以分成两个和相等的集合。
Input
第一行是一个正整数n,第二行每行n个正整数。
Output
输出一个数,表示方案数。
Sample Input
4
1 2 3 4
Sample Output
3
Hint
对于30%的数据,n<=10.
对于100%的数据,n<=20,a[i]<=100000000.
做法:注意到每个数的系数有三种情况,分别是0,1,-1。那么我们可以把序列分成长度相等的两部分,分别O(3^(n/2))搜索,排序后用双指针统计答案即可,统计时要注意判重。
代码如下:
#include <cstdio>
#include <algorithm>
#include <math.h>
using namespace std;
struct arr
{
int x,y;
}f[60007],ff[60007];
int n,a[30],e=0,ee=0,ans;
bool v[1024*1024+1];
bool bobo;
void dfs(int dep,int s,int ss)
{
if (s!=0) bobo=1;
if (dep>n/2 && bobo)
{
if (s==0 && !v[ss])
{
v[ss]=1;
ans++;
e++;
f[e].y=s;
f[e].x=ss;
}
else
{
e++;
f[e].y=s;
f[e].x=ss;
}
return;
}
if (dep>n/2) return;
dfs(dep+1,s,ss);
dfs(dep+1,s+a[dep],ss+pow(2,dep-1));
dfs(dep+1,s-a[dep],ss+pow(2,dep-1));
}
void dfs2(int dep,int s,int ss)
{
if (s!=0) bobo=1;
if (dep>n && bobo)
{
ee++;
ff[ee].y=s;
ff[ee].x=ss;
if (s==0 && !v[ss])
{
v[ss]=1;
ans++;
}
return;
}
if (dep>n) return;
dfs2(dep+1,s,ss);
dfs2(dep+1,s+a[dep],ss+pow(2,dep-1));
dfs2(dep+1,s-a[dep],ss+pow(2,dep-1));
}
int cmp(arr a,arr b)
{
return a.y<b.y;
}
int cmp2(arr a,arr b)
{
return a.y>b.y;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
bobo=false;
dfs(1,0,0);
bobo=false;
dfs2(n/2+1,0,0);
sort(f+1,f+e+1,cmp);
sort(ff+1,ff+ee+1,cmp2);
int i=1,j=1;
while (i<=e && j<=ee)
{
if (f[i].y+ff[j].y>0 && j<=ee) j++;
else if (f[i].y+ff[j].y<0 && i<=e) i++;
if (f[i].y+ff[j].y==0)
{
int ii=i,jj=j;
while (1==1)
{
if (!v[f[ii].x+ff[jj].x])
{
v[f[ii].x+ff[jj].x]=1;
ans++;
}
if (ff[jj].y==ff[jj+1].y) jj++;
else if (f[ii].y==f[ii+1].y)
{
ii++;
jj=j;
}
else break;
}
i=ii+1;
j=jj+1;
}
}
printf("%d",ans);
}