洛谷3067 BZOJ 2679题解(折半搜索)

传送门

BZOJ传送门(权限题)

看到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;
}

 

posted @ 2018-05-08 13:52  logeadd  阅读(314)  评论(0编辑  收藏  举报