考慮折半搜索,每個數的系數只能是-1,0,1之中的一個,因此可以先通過$O(3^\frac{n}{2})$的搜索分別搜索出兩邊每個狀態的和以及數字的選擇情況。
然后將后一半的狀態按照和排序,$O(2^\frac{n}{2})$枚舉前一半的每一個選擇情況的狀態,將該選擇情況下所有狀態按和排序,然后通過雙指針求出所有合法狀態。
時間復雜度$O(6^\frac{n}{2})$。
#include<cstdio> #include<algorithm> const int N=20,M=1<<10,E=59100; int n,n0,i,j,k,a[N],g[M],v[E],nxt[E],ed,m,q[M],ce,ans;bool vis[1<<N]; struct P{int s,S;}e[E]; inline bool cmp(const P&a,const P&b){return a.s<b.s;} void dfsl(int x,int s,int S){ if(x==n0){v[++ed]=s;nxt[ed]=g[S];g[S]=ed;return;} dfsl(x+1,s,S); dfsl(x+1,s+a[x],S|(1<<x)); dfsl(x+1,s-a[x],S|(1<<x)); } void dfsr(int x,int s,int S){ if(x==n){e[ce].s=s;e[ce++].S=S;return;} dfsr(x+1,s,S); dfsr(x+1,s+a[x],S|(1<<x)); dfsr(x+1,s-a[x],S|(1<<x)); } int main(){ scanf("%d",&n);n0=(n+1)/2; for(i=0;i<n;i++)scanf("%d",&a[i]); dfsl(0,0,0),dfsr(n0,0,0); std::sort(e+1,e+ce+1,cmp); for(i=0;i<1<<n0;i++){ for(m=0,j=g[i];j;j=nxt[j])q[m++]=v[j]; std::sort(q,q+m); for(j=k=0;j<ce;j++){ while(k<m&&q[k]<e[j].s)k++; if(k==m)break; if(q[k]==e[j].s)vis[i|e[j].S]=1; } } for(i=1;i<1<<n;i++)if(vis[i])ans++; return printf("%d",ans),0; }
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。