zoukankan      html  css  js  c++  java
  • 【BZOJ 2679】[Usaco2012 Open]Balanced Cow Subsets(折半搜索+双指针)

    [Usaco2012 Open]Balanced Cow Subsets

    题目描述

    给出(N(1≤N≤20))个数(M(i) (1 <= M(i) <= 100,000,000)),在其中选若干个数,如果这几个数可以分成两个和相等的集合,那么方案数加(1)

    求有多少种选数的方案。

    输入输出格式

    输入格式:

    * Line 1: The integer $ N$.

    * Lines 2..1+N: Line i+1 contains (M(i)).

    输出格式:

    * Line 1: The number of balanced subsets of cows.

    输入输出样例

    输入样例#1:

    4 
    1 
    2 
    3 
    4 
    

    输出样例#1:

    3 
    

    题解

    这道题算是一个折半搜索(meet in the middle)的好题

    如果对折半搜索不太熟悉,可以先做一道较简单的题 [CEOI2015 Day2]世界冰球锦标赛

    BZOJ链接洛谷链接 附加my blog

    这道题有三种状态

    1. 不放入任何集合
    2. 放入左边集合
    3. 放入右边集合

    在搜索时如何表示呢,我们可以0,1,-1来表示,代码如下:

    dfs(dep+1,sum);
    dfs(dep+1,sum+v[dep]);
    dfs(dep+1,sum-v[dep]);
    

    但是我们得到的答案可能会有重复,就是我们可能把一个数选入左集合或右集合,但是都加入了状态,所以我们需要判重。

    如何去判重,状态压缩,压成2进制去判重。

    所以搜索时还要去记录状态,用一个(vis)数组判重。

    if(!vis[a[l].state|b[r].state])
    	vis[a[l].state|b[r].state]=1;//state记录二进制的选数状态  1表示选 0表示没选
    

    最后要统计答案,排序后双指针扫描一遍即可。

    注意,最后别忘了把0的那种方案减去。

    code:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #define ll long long
    #define R register
    #define N 22
    using namespace std;
    template<typename T>inline void read(T &a){
        char c=getchar();T x=0,f=1;
        while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
        a=f*x;
    }
    int n,v[N<<1],maxdep,cnta,cntb;
    bool vis[1<<N];
    ll ans;
    struct node{
        int state,x;
    }a[1<<N],b[1<<N];
    inline bool cmp1(R node a,R node b){
        return a.x<b.x;
    }
    inline bool cmp2(R node a,R node b){
        return a.x>b.x;
    }
    inline void dfs(R int dep,R int sum,R int now,R int flg){
        if(dep==maxdep+1){
            if(!flg){
                a[++cnta].x=sum;
                a[cnta].state=now;
            }
            else{
                b[++cntb].x=sum;
                b[cntb].state=now;
            }
            return;
        }
        dfs(dep+1,sum,now,flg);
        dfs(dep+1,sum+v[dep],now+(1<<(dep-1)),flg);
        dfs(dep+1,sum-v[dep],now+(1<<(dep-1)),flg);
    }
    int main(){
        read(n);
        for(R int i=1;i<=n;i++)read(v[i]);
        maxdep=n/2;dfs(1,0,0,0);
        maxdep=n;dfs(n/2+1,0,0,1);
        sort(a+1,a+1+cnta,cmp1);
        sort(b+1,b+1+cntb,cmp2);
        R int l=1,r=1;
        while(l<=cnta&&r<=cntb){
            while(-a[l].x<b[r].x&&r<=cntb)r++;
            R int pos=r;
            while(r<=cntb&&-a[l].x==b[r].x){
                if(!vis[a[l].state|b[r].state]){
                    vis[a[l].state|b[r].state]=1;
                    ans++;
                }
                r++;
            }
            if(l<cnta&&a[l].x==a[l+1].x)r=pos;
            l++;
        }
        printf("%lld
    ",ans-1);
        return 0;
    }
    
  • 相关阅读:
    程序安装打包
    sql 2005 分页存储过程
    带线的无限级下拉树列表
    MapXtreme 2005 学习心得 概述(一)
    存储过程中用到的年,月,周的函数
    委托/事件/线程传参简单理解
    清除svn/vss小工具
    查看数据库连接数
    MapXtreme 2005 学习心得 使用WebTool工具(七)
    C#日期格式化
  • 原文地址:https://www.cnblogs.com/ZAGER/p/9823171.html
Copyright © 2011-2022 走看看