题意:有一堆数字,从中任意选一些,使得这些数字能够分成两组,两组和相等。
n<=20,ai<=10^9;
这题看起来简单,实际上比较难,暴力的话拿不了多少分。
正解的脑洞超大,我觉得我好弱啊。
首先我们要知道每个数都有3个系数,-1,0,1。
因此我们考虑折半搜索(别问我怎么考虑到的),因为只有3个系数,我们可以分别对前一半和后一半分别搜索,时间复杂度是3^(n/2),搜索出所有数字的选择情况。
然后对于后一半的状态,按照和排序,然后前一半的状态O(2^(N/2))枚举,在用双指针匹配前一半和后一半的状态统计答案。
ps:这题用pascal打有毒啊!!!!我本地秒过交上去就是RE0RE0,mdzz。改成c++240ms就过了,食屎啦你!( ・∀・)つ=≡≡ξ)Д`)。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int N=1e7;
ll m,tot,tot1,ans,n,midn,k;
bool vis[2*N];
int a[20],head[N],go[N],next[N],len[N];
struct node
{
ll x1,x2;
}edge[200000];
bool cmp1(node a,node b)
{
return a.x1<b.x1;
}
bool cmp2(int a,int b)
{
return a<b;
}
inline void dfs1(int x,int x1,int x2)
{
if (x==midn)
{
go[++tot]=x1;
next[tot]=head[x2];
head[x2]=tot;
return;
}
dfs1(x+1,x1,x2);
dfs1(x+1,x1+a[x],x2|(1<<x));
dfs1(x+1,x1-a[x],x2|(1<<x));
}
inline void dfs2(int x,int x1,int x2)
{
if (x==n)
{
edge[tot1].x1=x1;
edge[tot1++].x2=x2;
return;
}
dfs2(x+1,x1,x2);
dfs2(x+1,x1+a[x],x2|(1<<x));
dfs2(x+1,x1-a[x],x2|(1<<x));
}
int main()
{
freopen("subset.in","r",stdin);
freopen("subset.out","w",stdout);
scanf("%d",&n);
midn=(n+1)/2;
fo(i,0,n-1) scanf("%d",&a[i]);
dfs1(0,0,0);
dfs2(midn,0,0);
sort(edge+1,edge+1+tot1,cmp1);
fo(i,0,1<<midn)
{
m=0;
for(int j=head[i];j;j=next[j])
{
len[m++]=go[j];
}
sort(len,len+m,cmp2);
k=0;
fo(j,0,tot1-1)
{
while (k<m&&len[k]<edge[j].x1) k++;
if (k==m) break;
if (len[k]==edge[j].x1)
vis[i|edge[j].x2]=true;
}
}
fo(i,1,(1<<n)-1)
if (vis[i])ans++;
printf("%d\n",ans);
return 0;
}