二分图 + 二分图最大匹配

首先

hhs tql!!!!!!!!!

无意冒犯于lzb和hbc 但hhs这个是三个同学中我唯一一个听懂了的();

二分图

定义:

二分图又称作二部图,是图论中的一种特殊模型。
设G=(V, E)是一个无向图。如果顶点集V可分割为两个互不相交的子集X和Y,并且图中每条边连接的两个顶点一个在X中,另一个在Y中,则称图G为二分图。

上图:

 

 

 也就是说 X和Y互不干扰 独立成集 但XY之间有互通关系

 

性质:

当且仅当无向图G的每一个环的结数均为偶数时,G才是二分图。如果无环,相当于每个环的结点数为0,故也视为二分图。

 

判断是否是二分图:

这里我们用到染色法,用两种颜色来染这个图。如果该节点的颜色是0,那么所有与其相邻的节点的颜色都是1。最后判断是否满足上述条件即可。

然而在hhs巨佬给出的代码中 他用了 1 和 2 来进行染色 因此就有了 3 - a 这一步;

 

 

 

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <climits>
 6 #include <cmath>
 7 //#define int long long
 8 #define open(x); freopen("x.in","r",stdin);freopen("x.out","w",stdout);
 9 using namespace std;
10 inline int read(){
11     int x = 0,f = 1;
12     char ch = getchar();
13     while (ch < '0' || ch > '9'){
14         if (ch == '-') f = -1;
15         ch = getchar();
16     }
17     while (ch >= '0' && ch <= '9'){
18         x = x * 10 + ch - '0';
19         ch = getchar();
20     }
21     return x * f;
22 }
23 const int N = 1e5 + 10;
24 int n;
25 int m;
26 int head[N];
27 struct Edge{
28     int to;
29     int next;
30 }e[N];
31 int color[N];
32 int cnt = -1;
33 inline void add_edge(int u,int v){
34     e[++ cnt].to = v;
35     e[cnt].next = head[u];
36     head[u] = cnt;
37 }
38 inline bool dfs(int x,int c) {//判断 x 点是否可染 c 颜色
39     color[x] = c; //给 x 染 c 色, c = 1 or 2
40     for(register unsigned i = head[x];i;i = e[i].next){
41         int j = e[i].to;
42         if(!color[j]) //如果j点未被染色,判断它染另一种颜色是否合理。
43         if(!dfs(j,3-c)) return 0; //c = 1时,另一种颜色为 3 - 1 = 2
44         else if(color[j] == c) return 0; //如果 j 点与 x 点颜色相同, 则返回false
45     }
46     return 1;
47 }
48 signed main(){
49     n = read();
50     m = read(); 
51     memset(head,-1,sizeof(head));
52     for(register unsigned i(1);i <= m;i ++){
53         register int u = read();
54         register int v = read();
55         add_edge(u,v);
56         add_edge(v,u);
57     }
58     bool flag = 1;
59     for(register unsigned i(1);i <= n;i ++){//从1点开始,将未被染色的点染上1颜色。
60         if(!color[i]){
61             if(!dfs(i,1)){
62                 flag = 0;
63                 break;
64             }
65         }
66     }
67     if(flag) cout << "Yes" << endl;
68     else cout << "NO" << endl; 
69     return 0;
70 }
71  

二分图最大匹配:

首先什么是匹配:

 给定一个二分图G,在G的一个子图M中, M的边集{E}中的任意两条
边都不交汇于同一个结点,则称M是一个匹配。

举个例子(来自hhs巨佬的生动讲解):

我们把左点集看作是要买房子的人,右点集是正在出售的房子;

第一个人发现 当他来到第一栋房子的时候 第一栋房子还没有人来过且没有人买 那么第一栋房子就可以被标记为有人来过;

第二个人来到这里 发现这个房子有人来过了 于是就前往下一栋 也就是第三栋房子

当他发现这里没人来过的时候 他就是这栋房子的主人了

以此类推 我们要尽可能的让更多的人买到与之对应的唯一的房子

这就是最大匹配

那么如何求出最大匹配?

这里有一个很经典的算法:

匈牙利算法

算法流程:

1.

从任意一个没有被配对的点x开始,从点x的边中任意选一条边。

如果此时点i没有被配对那么配对成功,则找到了一条增广路。

如果点i此时已经被配对了,那么可以尝试将点i与其他点配对

。如果尝试成功,则找到一条增广路。

这里用match[ ]来记录配对关系, 即match[i] = x。

并且将配对数 + 1。 这个过程我们用dfs来实现。

2.

如果配对失败,就从点x的边中重选一条边尝试。直到点x配对成功或尝试完x所有的边。

3.

接下来对没有配对的点一一进行配对,直到所有的点都尝试完毕并找不到新的增广路。

 

上代码

 

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <climits>
 6 #include <cmath>
 7 #define int long long
 8 #define open(x); freopen("x.in","r",stdin);freopen("x.out","w",stdout);
 9 using namespace std;
10 inline int read(){
11     int x = 0,f = 1;
12     char ch = getchar();
13     while (ch < '0' || ch > '9'){
14         if (ch == '-') f = -1;
15         ch = getchar();
16     }
17     while (ch >= '0' && ch <= '9'){
18         x = x * 10 + ch - '0';
19         ch = getchar();
20     }
21     return x * f;
22 }
23 const int M = 5e4 + 5;
24 int n;//n表示左侧有n个点 
25 int m;//m表示右侧有m个点 
26 int E;//E表示共有E条边 
27 int ans = 0;
28 struct Edge{
29     int next;
30     int to;
31 }e[M];
32 int head[M];
33 int cnt = 0;
34 int vis[M];
35 int match[M];
36 inline void add_edge(int u,int v){
37     e[++ cnt].to = v;
38     e[cnt].next = head[u];
39     head[u] = cnt;
40 }
41 inline bool find(int x){
42     for(register unsigned i(head[x]);i;i = e[i].next){//遍历 
43     } 
44         int v = e[i].to;
45         if(!vis[v]){//当这栋房子没有人来过 
46             vis[v] = 1;//我来过了!! 
47             if(!match[v] || find(match[v])){//当这栋房子也没有人买 
48                 match[v] = x;//那他就是我的了!! 
49                 return 1;//这样我就买到房子了 一条路成功匹配 
50             }
51         }
52     }
53     return 0;//我没买到房子呜呜呜 匹配失败 
54 }
55 signed main(){
56     n = read();
57     m = read();
58     E = read();
59     for (register unsigned i(1);i <= E;i ++){
60         register int u = read();
61         register int v = read();
62         add_edge(u,v);
63     }
64     for(register unsigned i(1);i <= n;i ++){
65         memset(vis,false,sizeof(vis));
66         if(find(i)) ans ++;
67     }
68     cout << ans << endl;
69     return 0;
70 }

 

完结撒花(bushi

还有一个二分图带权最大匹配 暂时没搞出来 下次更

再来一遍

hhs tql!!!!!!

本博客大量借鉴于hhs博客 https://blog.csdn.net/glorious_dream/article/details/125034451?spm=1001.2014.3001.5501

24OI Fighting!!

 

posted @ 2022-06-05 11:31  November&&Rain  阅读(33)  评论(0)    收藏  举报