二分图的判定及最大匹配
如果一张无向图的N个结点可以分成A,B两个非空集合,其中$A\cap B=\emptyset$,并且在同一集合内的点之间都没有边相连,则称这张图为二分图。
二分图判定
定理:一张图是二分图,当且仅当图中不存在奇环(长度为奇数的环)。
染色法判定:尝试用黑白两色标记,当一个结点被标记后,它的所有相邻结点应该被标记与之相反的颜色,若该标记过程中出现冲突,则说明图中存在奇环。
下用BFS、DFS、并查集分别实现。
BFS1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
using namespace std;
const int N=1e4+10,M=2e5+10;
int head[N],ver[M],nex[M],vis[N];
int n,m,u,v,tot=0,ok=1;
int sum[5],ans;
void add(int u,int v)
{
ver[++tot]=v;
nex[tot]=head[u];
head[u]=tot;
}
bool bfs(int start,int color)
{
queue<int>q;
q.push(start);
vis[start]=color;
//printf("vis[%d]=%d\n",start,vis[start]);
sum[1]=1,sum[2]=0;//初始化
while(q.size())
{
int now=q.front();
q.pop();
for(int i=head[now];i;i=nex[i])
{
int to=ver[i];
if(vis[to]==0)
{
if(vis[now]==1) vis[to]=2,sum[2]++;
else vis[to]=1,sum[1]++;
//printf("vis[%d]=%d\n",to,vis[to]);
q.push(to);
}
else if(vis[to]==vis[now]) return false;
}
}
return true;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
for(int i=1;i<=n;i++)
{
if(vis[i]) continue;//vis[i]==0的话表明跟之前的结点都不连通
if(!bfs(i,1))
{
ok=0;
break;
}
else ans+=min(sum[1],sum[2]);//加和
}
if(ok) cout<<ans<<endl;
else cout<<"Impossible"<<endl;
return 0;
}
DFS1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
using namespace std;
const int N=1e4+10,M=2e5+10;
int head[N],ver[M],nex[M],vis[N];
int n,m,u,v,tot=0,ok=1;
int sum[5],ans;
void add(int u,int v)
{
ver[++tot]=v;
nex[tot]=head[u];
head[u]=tot;
}
bool dfs(int now,int color)
{
for(int i=head[now];i;i=nex[i])
{
int to=ver[i];
if(!vis[to])
{
if(vis[now]==1) vis[to]=2,sum[2]++;
else vis[to]=1,sum[1]++;
dfs(to,vis[to]);
}
else if(vis[to]==vis[now]) return false;
}
return true;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
for(int i=1;i<=n;i++)
{
if(vis[i]) continue;//vis[i]==0的话表明跟之前的结点都不连通
sum[1]=1,sum[2]=0;
vis[i]=1;
if(!dfs(i,1))
{
ok=0;
break;
}
else ans+=min(sum[1],sum[2]);//加和
}
if(ok) cout<<ans<<endl;
else cout<<"Impossible"<<endl;
return 0;
}
并查集二分图的判定(“扩展域”的并查集):
1 |
|
1 |
|
二分图最大匹配
匈牙利算法(增广路算法):
尝试给每个左部结点x匹配一个右部结点y。y能与x匹配的条件:
- y本身就是非匹配点;
- y已经与x’匹配,但从x’出发能找到另一个y’与x’匹配。
特点:当一个结点成为匹配点之后,至多因为找到增广路而更换匹配对象,但是不会变回非匹配点。
1 |
|