洛谷 P3067 [USACO12OPEN]Balanced Cow Subsets G
Description
P3067 [USACO12OPEN]Balanced Cow Subsets G
Solution
又是一道折半搜索题。
我们还是枚举一半的数,记录每一种情况的和。
然后爆搜另一半,当和为 \(S\) 时,判断前一半是否可以凑出 \(S\),如果可以,打个标记即可。
两次凑出 \(S\) 时,可能会有重复选用的数组,但其实不用管。
考虑到重复的话,两边同时删去同一个数也相等,但可能选用相同的数会多次出现,所以要打个标记。
\(dfs\) 过程中,状压记录一下当前选用了哪些数,加和减状压相同的状态也可以,前面证明过重复的数没有影响了。
这道题由于还需要找到和为 \(S\) 的状态有哪些,可以用一个 \(vector\) 存一下,但是和太大了。
所以还要用一个 \(map\) 离散化一下,不是真正的离散化,因为不需要排序,只需要标记一下即可。
代码还是比较容易理解的。
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
#include <vector>
#define ri register int
using namespace std;
const int N = 30;
const int S = (1 << 20) + 10;
int n, mid, cnt;
int a[N];
map <int, int> mp;
vector <int> g[S];
bool vis[S];
inline void dfs1(int x, int sum, int now){
// cout << "x " << x << " " << now << endl;
if(x > mid){
if(!mp.count(sum)) mp[sum] = ++cnt;
g[mp[sum]].push_back(now);
return;
}
dfs1(x + 1, sum, now);
dfs1(x + 1, sum + a[x], now | (1 << (x - 1)));
dfs1(x + 1, sum - a[x], now | (1 << (x - 1)));
}
inline void dfs2(int x, int sum, int now){
if(x > n){
if(mp.count(sum)){
int t = mp[sum];
for(auto x : g[t])
vis[x | now] = 1;
}
return;
}
dfs2(x + 1, sum, now);
dfs2(x + 1, sum + a[x], now | (1 << (x - 1)));
dfs2(x + 1, sum - a[x], now | (1 << (x - 1)));
}
int main(){
scanf("%d", &n);
for(ri i = 1; i <= n; i++)
scanf("%d", &a[i]);
mid = n >> 1;
dfs1(1, 0, 0), dfs2(mid + 1, 0, 0);
ri ans = 0;
for(ri i = 1; i <= (1 << n); i++)
ans += vis[i];
printf("%d\n", ans);
return 0;
}