1. 程式人生 > 實用技巧 >洛谷P2962 [USACO09NOV]Lights G 題解 折半搜尋/列舉

洛谷P2962 [USACO09NOV]Lights G 題解 折半搜尋/列舉

題目連結: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 ^= (1<<(u-1));
                for (int i = head[u]; i != -1; i = edge[i].nxt) {
                    int v = edge[i].v;
                    st ^= (1<<(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;
}