洛谷P2962 [USACO09NOV]Lights G 题解 折半搜索/枚举
之前的折半枚举的代码有一点点问题,已修改。 - 2020.10.3
题目链接:https://www.luogu.com.cn/problem/P2962
题目大意:
有 \(n(1 \le n \le 35)\) 盏灯,每盏灯与若干盏灯相连,每盏灯上都有一个开关,如果按下一盏灯上的开关,这盏灯以及与之相连的所有灯的开关状态都会改变。一开始所有灯都是关着的,你需要将所有灯打开,求最小的按开关次数。
解题思路:
http://oi-wiki.com/search/bidirectional/
基于这种思想,通过两次搜索把前一半和后一半的状态都枚举出来然后进行匹配。
但是我这里是基于状态压缩的思想将所有状态用循环枚举出来的。
示例代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 36, maxm = 1200;
map<long long, int> mp;
struct Edge {
int v, nxt;
Edge() {};
Edge(int _v, int _nxt) { v = _v; nxt = _nxt; }
} edge[maxm];
int n, m, head[maxn], ecnt, ans = INT_MAX;
void init() {
ecnt = 0;
memset(head, -1, sizeof(int)*(n+1));
}
void addedge(int u, int v) {
edge[ecnt] = Edge(v, head[u]); head[u] = ecnt ++;
edge[ecnt] = Edge(u, head[v]); head[v] = ecnt ++;
}
int main() {
scanf("%d%d", &n, &m);
init();
while (m --) {
int a, b;
scanf("%d%d", &a, &b);
addedge(a, b);
}
for (long long s = 0; s < (1<<(n/2)); s ++) {
long long st = 0;
for (int u = 1; u <= n/2; u ++) {
if ((1<<(u-1)) & s) {
st ^= (1LL<<(u-1));
for (int i = head[u]; i != -1; i = edge[i].nxt) {
int v = edge[i].v;
st ^= (1LL<<(v-1));
}
}
}
long long st2 = 0;
for (int i = 0; i < n; i ++) if (!(st & (1LL<<i))) st2 ^= (1LL<<i);
if (mp.find(st2) == mp.end()) mp[st2] = __builtin_popcount(s);
else mp[st2] = min(mp[st2], __builtin_popcount(s));
}
for (long long s = 0; s < (1<<(n-n/2)); s ++) {
long long st = 0;
for (int u = n/2+1; u <= n; u ++) {
if ((1<<(u-n/2-1)) & s) {
st ^= (1LL<<(u-1));
for (int i = head[u]; i != -1; i = edge[i].nxt) {
int v = edge[i].v;
st ^= (1LL<<(v-1));
}
}
}
if (mp.find(st) != mp.end()) ans = min(ans, mp[st] + __builtin_popcount(s));
}
printf("%d\n", ans);
return 0;
}
折半搜索代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 36;
map<long long, int> mp;
vector<int> g[maxn];
bool color[maxn];
int n, m, ans = -1;
void paint(int u) {
color[u] = !color[u];
int sz = g[u].size();
for (int i = 0; i < sz; i ++) {
int v = g[u][i];
color[v] = !color[v];
}
}
void dfs1(int u, int cnt) {
if (u > n/2) {
long long st = 0;
for (int i = 1; i <= n; i ++) {
if (!color[i]) st ^= (1LL<<(i-1));
}
if (mp.find(st) != mp.end()) {
mp[st] = min(mp[st], cnt);
}
else mp[st] = cnt;
return;
}
dfs1(u+1, cnt);
paint(u);
dfs1(u+1, cnt+1);
paint(u);
}
void dfs2(int u, int cnt) {
if (u > n) {
long long st = 0;
for (int i = 1; i <= n; i ++) {
if (color[i]) {
st ^= (1LL<<(i-1));
}
}
if (mp.find(st) != mp.end()) {
int tmp = mp[st] + cnt;
if (ans == -1 || ans > tmp) ans = tmp;
}
return;
}
dfs2(u+1, cnt);
paint(u);
dfs2(u+1, cnt+1);
paint(u);
}
int main() {
cin >> n >> m;
while (m --) {
int a, b;
cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
dfs1(1, 0);
dfs2(n/2+1, 0);
cout << ans << endl;
return 0;
}