題目:[https://www.luogu.com.cn/problem/P2962] (P2962 [USACO09NOV] Lights G)
演算法:meet in middle(折半搜尋)
思路:
有\(35\)個點,總共的操作狀態有\(2^{35}\)種情況。如果我們採用一般的搜尋方式,時間上會毫不猶豫得爆掉。 所以,我們要用折半搜尋的方式。將所有的點拆分成兩個集合,對兩個集合分別用搜尋的方式(列舉所有可能出現的情況)。這樣就只有\(O(2^{n/2})\)的時間複雜度了!也能順利解決問題!
注意的細節:
1、用 $ long long $ 代替 \(int\) 用來提高資料的承受能力。
2、提前預處理 \(2\) 的指數。
3、用 \(count\) 函式來計算\(1\)的個數。
4、對 << 與 >> 的正確使用。
點選檢視程式碼
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
using namespace std;
int n, m, ans = 0x7fffffff;
map<long long, int> f;
long long a[40];
int main() {
cin >> n >> m;
a[0] = 1;
for (int i = 1; i < n; ++i) a[i] = a[i - 1] * 2; // 進行預處理
for (int i = 1; i <= m; ++i) { // 對輸入的邊的情況進行處理
int u, v;
cin >> u >> v;
--u;
--v;
a[u] |= ((long long)1 << v);
a[v] |= ((long long)1 << u);
}
for (int i = 0; i < (1 << (n / 2)); ++i) { // 對前一半進行搜尋
long long t = 0;
int cnt = 0;
for (int j = 0; j < n / 2; ++j) {
if ((i >> j) & 1) {
t ^= a[j];
++cnt;
}
}
if (!f.count(t))
f[t] = cnt;
else
f[t] = min(f[t], cnt);
}
for (int i = 0; i < (1 << (n - n / 2)); ++i) { // 對後一半進行搜尋
long long t = 0;
int cnt = 0;
for (int j = 0; j < (n - n / 2); ++j) {//注意這裡的處理
if ((i >> j) & 1) {
t ^= a[n / 2 + j];//連鎖
++cnt;
}
}
if (f.count((((long long)1 << n) - 1) ^ t))
ans = min(ans, cnt + f[(((long long)1 << n) - 1) ^ t]);
}
cout << ans;
return 0;
}