洛谷 P3067 [USACO12OPEN]Balanced Cow Subsets G 折半搜索
题目
https://www.luogu.com.cn/problem/P3067
思路
考虑折半搜索,第一个dfs对[1,n/2]的数进行分组,+代表第一组,-代表第二组,并计算两组总和的情况方案数。
第二个dfs对[n/2+1,n]的数统计数字和为sum的方案,同时与前n/2个数的方案进行匹配,寻找中和为-sum的方案。两者和为1代表一种结果。
分组的去重方法:
考虑状态压缩,给每一个数分配一个二进制位(1代表选择,0代表不选),那么对应的每一种方案数获得一种二进制编号。
将和对应的二进制编号拼在一起,并判断重复即可。因为,所以只需要开10位的空间留给的编号。
时间复杂度,不开O2要T2个点。
因为push_back()均摊复杂度是,改成手写大概就行了(?)
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!