常见问题
最小点覆盖、最大独立集、最小路径点覆盖(最小路径重复点覆盖);
注意
二分图一般是指无向图,后续例题虽然存在有向图,但是思路还是无向图的;
常见性质
- 一个图是二分图等价于图中不存在奇数环等价于染色法不存在矛盾
- 最大匹配数 = 最小点覆盖 = 总点数 - 最大独立集 = 总点数 - 最小路径覆盖
例题
关押罪犯
传送门
二分 + 染色法判定二分图
题面
思路
把罪犯看成点,把仇恨关系看成边,仇恨值看成边权;
那么问题就转化为将所有点分成两组,使得各组内边的权重的最大值尽可能小;
因为这是一个最大值最小化的问题,我们考虑用二分来解决;
假设当前二分的限制为mid
;
判断能否将所有点分成两组,使得所有权值大于mid
的边都在组间,而不在组内。也就是判断由所有点以及所有权值大于mid
的边构成的新图是否是二分图;
判断二分图我们使用染色法,时间复杂度为 O ( n + m ) O(n+m) O(n+m), n n n是点数, m m m是边数;
那么当 m i d ∈ [ a n s , 1 0 9 ] mid∈[ans,10^9] mid∈[ans,109]时,所有边权大于 m i d mid mid的边,必然是所有边权大于 a n s ans ans的边的子集,因此由此构成的新图也是二分图。
当 m i d ∈ [ 0 , a n s − 1 ] mid∈[0,ans−1] mid∈[0,ans−1]时,由于 a n s ans ans是新图可以构成二分图的最小值,因此由大于 m i d mid mid的边构成的新图一定不是二分图。
Code
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <utility>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 2e4 + 10;
int n,m;
vector<pii> G[N];
int color[N];//-1未染色,0白色,1黑色
bool dfs(int u,int c,int val){
color[u] = c;
for(auto to : G[u]){
int v = to.first,w = to.second;
if(w <= val) continue;//只考虑边权>val的边
if(color[v] != -1){
if(color[v] == c) return false;
}
else{//v未被染色,我们去染v
if(!dfs(v,c^1,val)) return false;
}
}
return true;
}
bool check(int val){
//染色法判断二分图
for(int i=1;i<=n;++i) color[i] = -1;
for(int i=1;i<=n;++i)
if(color[i] == -1)
if(!dfs(i,0,val)) return false;
return true;
}
void solve(){
cin >> n >> m;
while(m--){
int u,v,w;
cin >> u >> v >> w;
G[u].push_back({v,w});
G[v].push_back({u,w});
}
int L = 0,R = 1e9;
while(L < R){
int mid = (L + R) >> 1;
if(check(mid)){
//只考虑边权大于mid的边,能构成二分图
R = mid;
}
else L = mid + 1;
}
cout << L << '\n';
}
signed main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
solve();
return 0;
}