题目大意:
给你一个含n个点的无向图,每个点有状态0/1。按下某个点,它以及周围的点状态都会改变,初始全为0.问你最少多少次操作到达全1状态.
n
≤
35
n \leq 35
n≤35
题目思路:
首先,每个点最多只会操作一次.因为同一个点操作两次等于没有操作.而且操作与顺序无关.所以操作方案总共有 2 n 2^n 2n个.直接搜索显然不行.我们采用MITM.
枚举前 2 n 2 2^{\frac{n}{2}} 22n个点的所有方案,我们模拟一遍位运算得到其状态去用map更新最小操作数.
再枚举后 2 n 2 2^{\frac{n}{2}} 22n个点的所有方案,我们模拟一遍位运算求补集查表更新答案.
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll a[40];
unordered_map<ll , int> f;
int main()
{
ios::sync_with_stdio(false);
int n , m; cin >> n >> m;
for (int i = 0 ; i < n ; i++) a[i] = (1ll << i);
for (int i = 1 ; i <= m ; i++){
int x , y;cin >> x >> y;
x-- , y--;
a[x] |= (1ll << y);
a[y] |= (1ll << x);
}
int cnt = n / 2;
int s = 1 << cnt;
for (int i = 0 ; i < s ; i++){
ll now = 0 , cost = 0;
for (int j = 0 ; j < cnt ; j++)
if (i & (1 << j)) now ^= a[j] , cost++;
if (f.find(now) == f.end()) f[now] = cost;
else f[now] = min (1ll * f[now] , cost);
}
cnt = n - cnt;
s = 1 << cnt;
ll ans = 1e9;
for (int i = 0 ; i < s ; i++){
ll now = 0 , cost = 0;
for (int j = 0 ; j < cnt ; j++)
if (i & (1 << j)) now ^= a[n / 2 + j] , cost++;
if (f.find( ((1ll << n) - 1) ^ now ) != f.end())
ans = min (ans , cost + f[((1ll << n) - 1) ^ now]);
}
cout << ans << endl;
return 0;
}