洛谷 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;
}

End

posted @ 2021-10-08 15:14  xixike  阅读(125)  评论(0编辑  收藏  举报