洛谷3067 BZOJ 2679题解(折半搜索)
看到n小于20,就可以想到搜索
所有的数要么在集合a中,要么在集合b中,要么都不在
可是3^n复杂度会炸,我们考虑优化
可以利用折半搜索,将前面一半的所有可能情况与后一半列举
排序扫描统计答案
由于选择情况可能会重复,我们还要记录一下状态,然后在统计时判断一下
统计时会将一个都不选的情况计算进去,所以ans要-1
# include<iostream> # include<cstdio> # include<cmath> # include<algorithm> # include<cstring> using std::sort; const int mn = 21; int a[mn]; int n; struct node{int val,cur;}; node L[1<<mn],R[1<<mn]; int vis[1<<mn]; int LeftCnt,RightCnt; void dfs(int x,int en,int nowval,int nowstate) { if(x>en) { if(en==n/2) L[++LeftCnt].val=nowval,L[LeftCnt].cur=nowstate; else R[++RightCnt].val=nowval,R[RightCnt].cur=nowstate; return ; } dfs(x+1,en,nowval,nowstate); dfs(x+1,en,nowval-a[x],nowstate+(1<<(x-1))); dfs(x+1,en,nowval+a[x],nowstate+(1<<(x-1))); } bool cmp1(node x,node y){return x.val<y.val;} bool cmp2(node x,node y){return x.val>y.val;} int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); dfs(1,n/2,0,0); dfs(n/2+1,n,0,0); sort(L+1,L+1+LeftCnt,cmp1); sort(R+1,R+1+RightCnt,cmp2); int l=1,r=1,ans=0; while(l<=LeftCnt && r<=RightCnt) { while(L[l].val+R[r].val>0 && r<=RightCnt) r++; int pre=r; while(L[l].val+R[r].val==0 && r<=RightCnt) { if(vis[L[l].cur | R[r].cur]==0) vis[L[l].cur | R[r].cur]=1,ans++; r++; } if(L[l].val==L[l+1].val) r=pre; l++; } printf("%d",ans-1); return 0; }