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

Farmer John 面临一个挑战:他的新挤奶机要求奶牛左侧和右侧的产奶量相同。他想知道有多少种平衡的奶牛子集。给定每头奶牛的产奶量,你需要计算可以分成两个等和组的奶牛子集数量。通过使用 Meet in the Middle 策略和二进制状态压缩来优化解决方案,对两个数组分别升序和降序排序,然后使用双指针方法计算符合条件的子集数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

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<bits/stdc++.h>
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);
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值