二分图
定义
如果一张无向图可以分为两个集合,并且两集合内部的店没有边相连,则称该图为二分图。
判定
染色法
任意选择一个点开始染色,将与它相连的点染上相反的颜色,当出现矛盾时,说明不是二分图,否则为二分图。
例题:P1525 [NOIP2010 提高组] 关押罪犯
题目中将罪犯分为两组,同一监狱之间才会产生仇恨,将其抽象为图的边。如果将这些边删去,恰好为二分图。
求最大仇恨的最小值,设为 ans,显然可以二分。
我们将小于 ans 的边删去,保留大于 ans 的边,我们可以发现若此时图为二分图,则此 mid 成立,我们可以继续将 r 减小。
注意,图可能不连通。
时间复杂度为 \(O((n+m)\times \log max(c_i))\)代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 2e4+5;
vector<pair<int,int> > e[N];
void add(int u,int v,int w){
e[u].push_back(make_pair(v,w));
e[v].push_back(make_pair(u,w));
}
int color[N];
int n,m,maxn = -1;
bool flag = true;
void dfs(int now,int now_color,int mid){
if(flag == false){
return;
}
color[now] = now_color;
for(int i=0;i<e[now].size();i++){
if(e[now][i].second <= mid){
continue;
}
int v = e[now][i].first;
if(color[v] == 0){
dfs(v,3-now_color,mid);
}else if(color[v] == now_color){
flag = false;
}
}
return;
}
bool check(int mid){
for(int i=1;i<=n;i++){
if(color[i] == 0){
dfs(i,1,mid);
}
}
return flag;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
maxn = max(maxn,w);
add(u,v,w);
}
int l = 0,r = maxn+1;
while(l < r){
flag = true;
memset(color,0,sizeof color);
int mid = (l+r)>>1;
if(check(mid)){
r = mid;
}else{
l = mid + 1;
}
}
printf("%d",r);
}
二分图的最大匹配
定义
匹配:设 G 为二分图,若其子图 M 满足任意两条边之间无公共节点,则为二分图的匹配。
最大匹配:当 M 的边数最多时,为二分图的最大匹配。
匹配边:属于 M 的边,非匹配边同理。
交替路:从一个不属于 M 的点出发,依次经过不属于 M 的边、属于 M 的边……所形成的链为交替路。
增广路:从一个不属于 M 的点出发,走交替路,若能到达另一个不属于 M 的点,则经过的路径成为增广路。
增广路的性质
非匹配边数 - 匹配边数 = 1。
交换增广路的匹配边与非匹配边时,我们发现,匹配边多了一条,且仍然符合匹配的性质。
匈牙利算法
不断寻找增广路,将最广路上的匹配边与非匹配边互换,直到找不到增广路为止,用邻接表或动态数组存图时间复杂度为 \(O(nm)\),用邻接矩阵存图时间复杂度为 \(O(n^3)\)。
模板
模板代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e3+5;
vector<int> edge[N];
bool vis[N];
int match[N];
void add(int u,int v){
edge[u].push_back(v);
}
bool dfs(int now){
for(int i=0;i<edge[now].size();i++){
int v = edge[now][i];
if(vis[v]){
continue;
}
vis[v] = true;
if(!match[v] || dfs(match[v])){
match[v] = now;
return true;
}
}
return false;
}
int main(){
int n,m,e;
scanf("%d%d%d",&n,&m,&e);
for(int i=1;i<=e;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v+n);
}
int ans = 0;
for(int i=1;i<=n;i++){
memset(vis,0,sizeof vis);
if(dfs(i)){
ans ++;
}
}
printf("%d",ans);
}
2倍经验。
例题:P1640 [SCOI2010] 连续攻击游戏
本题理论上匈牙利算法时间复杂度为 \(O(nm)\) 但实际上可以过。
本题理论正解为网络最大流,时间复杂度为 \(O(m\sqrt{n})\)。
网络最大流
二分图的最大匹配可以转化为网络流模型。

浙公网安备 33010602011771号