洛谷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;
}

 

转载于:https://www.cnblogs.com/logeadd/p/9007794.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值