1. 程式人生 > 其它 >SP11469 SUBSET - Balanced Cow Subsets

SP11469 SUBSET - Balanced Cow Subsets

Description

洛谷傳送門

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

本文來自部落格園,作者:{xixike},轉載請註明原文連結:https://www.cnblogs.com/xixike/p/15379937.html