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

原文地址:https://www.cnblogs.com/quanjun/p/13685993.html