HZOJ 毛一琛
直接暴搜是$3^n$的,无法接受。
正解是$meet \ in \ the \ middle$,暴搜前n/2个数,每个数的状态有三种:都不选,选为A集合,选为B集合。那么我们可以维护两个集合的差。
设状态为sta,每个数选中为1(无论是A还是B集合都为1),否则为0。差为v。
将二元组(sta,v)插入Hash_map。
之后暴搜后n/2个数。同样统计出状态sta和差v。在Hash_map中查询差为v的二元组个数。同时用数组v[1<<11][1<<11]记录两个状态是否选择过去重。
#include<bits/stdc++.h> #define LL long long using namespace std; bool v[1<<11][1<<11]; struct Hash_map { int fi[2333333],ni[2333333],siz; LL key[2333333],val[2333333]; inline void insert(int x,int y) { int k=(x%2333333+2333333)%2333333,i=fi[k]; for(;i;i=ni[i])if(key[i]==x&&val[i]==y)return; i=++siz,key[i]=x,val[i]=y,ni[i]=fi[k],fi[k]=i; } inline int find(int x,int y) { int k=(x%2333333+2333333)%2333333,res=0; for(int i=fi[k];i;i=ni[i]) if(key[i]==x&&!v[y][val[i]])v[y][val[i]]=1,res++; return res; } }f; int n,m[21]; LL ans=0; void dfs(int now,int en,int sta,int vv) { if(now==en+1) { if(en!=n)f.insert(vv,sta); else ans+=f.find(vv,sta); return; } dfs(now+1,en,sta<<1,vv); dfs(now+1,en,sta<<1|1,vv+m[now]); dfs(now+1,en,sta<<1|1,vv-m[now]); } signed main() { // freopen("in.txt","r",stdin); // freopen("1.out","w",stdout); cin>>n;for(int i=1;i<=n;i++)cin>>m[i]; dfs(1,n/2,0,0);dfs(n/2+1,n,0,0); printf("%lld\n",ans-1); }
波澜前,面不惊。