洛谷 P3067 [USACO12OPEN]Balanced Cow Subsets G 折半搜索

题目

https://www.luogu.com.cn/problem/P3067

思路

考虑折半搜索,第一个dfs对[1,n/2]的数进行分组,+代表第一组,-代表第二组,并计算两组总和的情况方案数ai

第二个dfs对[n/2+1,n]的数统计数字和为sum的方案bi,同时与前n/2个数的方案进行匹配,寻找ai中和为-sum的方案。两者和为1代表一种结果。

分组的去重方法:

考虑状态压缩,给每一个数分配一个二进制位(1代表选择,0代表不选),那么对应的每一种方案数获得一种二进制编号。

aibi对应的二进制编号拼在一起,并判断重复即可。因为n20,所以只需要开10位的空间留给bi的编号。

时间复杂度O(n3n/2+3n/2log3n/2),不开O2要T2个点。

因为push_back()均摊复杂度是O(n),改成手写大概就行了(?)

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<unordered_map>

const int maxm = (1 << 20) + 10;
const int maxn = 30 + 5;
std::unordered_map<long long, std::vector<int> > mp;
int num[maxn];
int n;
long long ans;
bool vis[maxm];

void dfs1(int x, int sum, int border, int id) {
    if (x > border) {
        mp[sum].push_back(id);
        return;
    }
    dfs1(x + 1, sum + num[x], border, (id << 1) | 1);//左侧
    dfs1(x + 1, sum - num[x], border, (id << 1) | 1);//右侧
    dfs1(x + 1, sum, border, id << 1);//不选
}

void dfs2(int x, int sum, int border, int id) {
    if (x > border) {
        for (int i: mp[-sum]) {
            if (vis[(i << 10) | id]) continue;//去重
            vis[(i << 10) | id] = 1;
            ans++;
        }
        return;
    }
    dfs2(x + 1, sum + num[x], border, (id << 1) | 1);//左侧
    dfs2(x + 1, sum - num[x], border, (id << 1) | 1);//右侧
    dfs2(x + 1, sum, border, id << 1);//不选
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &num[i]);
    }
    dfs1(1, 0, n / 2, 0);
    dfs2(n / 2 + 1, 0, n, 0);
    std::cout << ans - 1;
}
posted @   SxtoxA  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
12 13
点击右上角即可分享
微信分享提示