【搜索】【Meet in the middle】【Usaco2012 Open】Balanced Cow Subsets

题目描述

Farmer John's owns N cows (2 <= N <= 20), where cow i produces M(i) units of milk each day (1 <= M(i) <= 100,000,000). FJ wants to streamline the process of milking his cows every day, so he installs a brand new milking machine in his barn. Unfortunately, the machine turns out to be far too sensitive: it only works properly if the cows on the left side of the barn have the exact same total milk output as the cows on the right side of the barn!

Let us call a subset of cows "balanced" if it can be partitioned into two groups having equal milk output. Since only a balanced subset of cows can make the milking machine work, FJ wonders how many subsets of his N cows are balanced. Please help him compute this quantity.

给n个数,从中任意选出一些数,使这些数能分成和相等的两组。

求有多少种选数的方案。

输入

* Line 1: The integer N.

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

输出

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

输入样例

4 
1 
2 
3 
4 

输出样例

3 

说明

There are 4 cows, with milk outputs 1, 2, 3, and 4.

There are three balanced subsets: the subset {1,2,3}, which can be partitioned into {1,2} and {3}, the subset {1,3,4}, which can be partitioned into {1,3} and {4}, and the subset {1,2,3,4} which can be partitioned into {1,4} and {2,3}.

分析

首先分析题目

对于每个数有三种状态

    不选

    选入左边集合

    选入右边集合

对应的我们将这个数的系数变为

    0

    1

    -1

那么最后我们要找的就是满足左边加右边等于0的方案数

观察数据范围,直接暴力搜索是不行的

考虑 meet in the middle 进行优化

我们把所有的数分成两部分,分别按照上面的方法处理出所有方案

然后统计答案

由于我们可能会重复加入某个数字,同时考虑N的范围较小,使用二进制状压维护一个vis

每次将前半部分和后半部分的状态取交(“|”,只要有一个为1返回1)就得到了总的状态,然后把它标记就行

统计答案时我们将一个数组升序排序,另外一个数组降序排序

然后利用双指针扫描法(自己起的名字。嘿嘿嘿)进行处理。(具体结合代码)

代码

#include
using namespace std;
struct node {
    int zt, a;
} a[(1 << 20) + 10], b[(1 << 20) + 10];

int n;
int v[47];
int cnta, cntb;
bool vis[(1 << 20) + 10];
long long ans;


inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
    return x * f;
}

bool cmp1(node x, node y) {
    return x.a < y.a;
}

bool cmp2(node x, node y) {
    return x.a > y.a;
}

void dfs1(int dep, int sum, int now) {
    if (dep == n / 2 + 1) {
        a[++cnta].a = sum;
        a[cnta].zt = now;
        return;
    }
    dfs1(dep + 1, sum, now);//不选
    dfs1(dep + 1, sum + v[dep], now + (1 << (dep - 1)));//选入集合一 
    dfs1(dep + 1, sum - v[dep], now + (1 << (dep - 1)));//选入集合二 
}

void dfs2(int dep, int sum, int now) {
    if (dep == n + 1) {
        b[++cntb].a = sum;
        b[cntb].zt = now;
        return;
    }
    dfs2(dep + 1, sum, now);//不选 
    dfs2(dep + 1, sum + v[dep], now + (1 << (dep - 1)));//选入集合一 
    dfs2(dep + 1, sum - v[dep], now + (1 << (dep - 1)));//选入集合二 
}

int main() {
    n = read();
    for (int i = 1; i <= n; i++) v[i] = read();
    dfs1(1, 0, 0);
    dfs2(n / 2 + 1, 0, 0);
    sort(a + 1, a + cnta + 1, cmp1);
    sort(b + 1, b + cntb + 1, cmp2);
    int l = 1, r = 1;
    while (l <= cnta && r <= cntb) {
        while (r <= cntb && -a[l].a < b[r].a) r++;
        int tmp = r;
        while (r <= cntb && -a[l].a == b[r].a) {
            if (!vis[a[l].zt | b[r].zt]) {
                vis[a[l].zt | b[r].zt] = true;
                ans++;
            }
            r++;
        }
        if (l < cnta && a[l].a == a[l + 1].a) r = tmp;
        l++;
    }
    printf("%lld", ans - 1);
}


 

你可能感兴趣的:(搜索)