题目:[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;
}