SPOJ11469 Subset(折半枚举)
题意
给定一个集合,有多少个非空子集,能划分成和相等的两份。$n\leq 20$
题解
看到这个题,首先能想到的是$3n$的暴力枚举,枚举当前元素是放入左边还是放入右边或者根本不放,但是显然是不可取的,看到$n$只有20,考虑折半搜索,将集合分成两部分,每个部分$3{\frac{n}{2}}$枚举。
接着考虑如何合并,在枚举时计一个$delta$表示此时左边和右边的差值,这样在右半部分每一次枚举完后我们可以直接在左半部分查找是否存在一个$delta$相等,如果相等,则两个集合的并集满足条件
#include <map>
#include <vector>
#include <cstdio>
typedef long long ll;
template <typename T>
inline void read(T &x) {
x = 0; char ch = getchar();
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
}
const int N = 21;
int n, m, a[N], cnt, ans;
std::map <int, int> Ma;
std::vector <int> S[1 << N];
bool ok[1 << N];
void dfs1(int i, int s, int d) {
if (i > m) {
if (Ma.find(d) == Ma.end()) Ma[d] = ++cnt;
int index = Ma[d];
//记录delta,由于可能存在多个相等的delta,开一个vector记下它是哪个集合(状态压缩)
S[index].push_back(s);
return ;
}
dfs1(i + 1, s, d);
dfs1(i + 1, s | (1 << i), d + a[i]);
dfs1(i + 1, s | (1 << i), d - a[i]);
}
void dfs2(int i, int s, int d) {
if (i > n) {
if (Ma.find(d) == Ma.end()) return ;
int index = Ma[d];
std::vector<int>::iterator it;
//直接查询然后置他们的并集为真即可
for (it = S[index].begin(); it != S[index].end(); ++it)
ok[*it | s] = true;
return ;
}
dfs2(i + 1, s, d);
dfs2(i + 1, s | (1 << i), d + a[i]);
dfs2(i + 1, s | (1 << i), d - a[i]);
}
int main () {
read(n); m = n >> 1;
for(int i = 1; i <= n; ++i) read(a[i]);
dfs1(1, 0, 0);
dfs2(m + 1, 0, 0);
for(int i = (1 << (n + 1)) - 1; i >= 1; --i)
ans += ok[i];
printf("%d\n", ans);
return 0;
}
SPOJ11469 Subset(折半枚举)的更多相关文章
- POJ 3977 Subset(折半枚举+二分)
SubsetTime Limit: 30000MS Memory Limit: 65536KTotal Submissions: 6754 Accepted: 1277 D ...
- POJ3977 Subset 折半枚举
题目大意是给定N个数的集合,从这个集合中找到一个非空子集,使得该子集元素和的绝对值最小.假设有多个答案,输出元素个数最少的那个. N最多为35,假设直接枚举显然是不行的. 可是假设我们将这些数分成两半 ...
- POJ 3977 - subset - 折半枚举
2017-08-01 21:45:19 writer:pprp 题目: • POJ 3977• 给定n个数,求一个子集(非空)• 使得子集内元素和的绝对值最小• n ≤ 35 AC代码如下:(难点:枚 ...
- poj 3977 Subset(折半枚举+二进制枚举+二分)
Subset Time Limit: 30000MS Memory Limit: 65536K Total Submissions: 5721 Accepted: 1083 Descripti ...
- Load Balancing 折半枚举大法好啊
Load Balancing 给出每个学生的学分. 将学生按学分分成四组,使得sigma (sumi-n/4)最小. 算法: 折半枚举 #include <iostrea ...
- CSU OJ PID=1514: Packs 超大背包问题,折半枚举+二分查找。
1514: Packs Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 61 Solved: 4[Submit][Status][Web Board] ...
- NYOJ 1091 超大01背包(折半枚举)
这道题乍一看是普通的01背包,最最基础的,但是仔细一看数据,发现普通的根本没法做,仔细观察数组发现n比较小,利用这个特点将它划分为前半部分和后半部分这样就好了,当时在网上找题解,找不到,后来在挑战程序 ...
- Codeforces 888E - Maximum Subsequence(折半枚举(meet-in-the-middle))
888E - Maximum Subsequence 思路:折半枚举. 代码: #include<bits/stdc++.h> using namespace std; #define l ...
- Codeforces 912 E.Prime Gift (折半枚举、二分)
题目链接:Prime Gift 题意: 给出了n(1<=n<=16)个互不相同的质数pi(2<=pi<=100),现在要求第k大个约数全在所给质数集的数.(保证这个数不超过1e ...
- poj_3977 折半枚举
题目大意 给定N(N<=35)个数字,每个数字都<= 2^15. 其中一个或多个数字加和可以得到s,求出s的绝对值的最小值,并给出当s取绝对值最小值时,需要加和的数字的个数. 题目分析 需 ...
随机推荐
- POJ 2891- Strange Way to Express Integers CRT 除数非互质
题意:给你余数和除数求x 注意除数不一定互质 思路:不互质的CRT需要的是将两个余数方程合并,需要用到扩展GCD的性质 合并互质求余方程 m1x -+ m2y = r2 - r1 先用exgcd求出特 ...
- 第八周 yukun 20155335
- Different Integers(牛客多校第一场+莫队做法)
题目链接:https://www.nowcoder.com/acm/contest/139/J 题目: 题意:给你n个数,q次查询,对于每次查询得l,r,求1~l和r~n元素得种类. 莫队思路:1.将 ...
- Git 常用命令(二)
用 git init 在目录中创建新的 Git 仓库. $ mkdir test $ cd test/ $ git init Initialized empty Git repository in ...
- 灵活使用ARM汇编的WEAK关键字
//=====================================================================//TITLE:// 灵活使用ARM汇编的WEAK关 ...
- python中BeautifulSoup模块
BeautifulSoup模块是干嘛的? 答:通过html标签去快速匹配标签中的内容.效率相对比正则会好的多.效率跟xpath模块应该差不多. 一:解析器: BeautifulSoup(html,&q ...
- 【Matlab】使用Matlab运行Windows命令
可以使用Matlab的一些命令来帮助程序运行.比如说 ! calc % 打开计算器 ! mspaint % 打开画图 dos calc % 打开计算器 比如一个程序要运行很长时间,而我们又不能一直守在 ...
- Which cryptsetup
Which cryptsetup Rpm –qf ‘which cryptsetup’ 安装加密工具: 设置加密分区 Crptsetup luksFormat Echo –n “xuegod123” ...
- python基础===Number
本文转自:python之Number 1.Python number数字 Python Number 数据类型用于存储数值. 数据类型是不允许改变的,这就意味着如果改变 Number 数据类型的值,将 ...
- Linux内核基础--事件通知链(notifier chain)【转】
转自:http://blog.csdn.net/wuhzossibility/article/details/8079025 内核通知链 1.1. 概述 Linux内核中各个子系统相互依赖,当其中某个 ...