0)二分图匹配有两种算法,一种是网络流中的最大流,另一种就是下面的匈牙利算法。
①用染色法判断是否是二分图:
主要是怎么判断一个图是不是二分图。不妨选取某个点作为起点并染为某种颜色、同时把与它相邻的元素染为对立的颜色,进行BFS(DFS也可以),如果到某一步发现当前点和相邻点的颜色一样,那么就出现了矛盾,就不是二分图。
②匈牙利算法求最大匹配数
原理 是
通过DFS或者BFS进行遍历,遇到新的未匹配的点或者有增广路(若有增广路就可以反转,则最大匹配数就可以+1)的出现,便返回TRUE使当前最大匹配数+1,然后继续遍历,直到完成遍历或者已找出最大匹配数。
流程 是
建立一张二分图,左右两边都是图的所有节点,取左端节点。(题目中可能已直接给出二分图;也可能题目中只给出所有相连点对构成的图,可以用用链表存储边到边的关系,如下面的示例代码,如果每条边按两个方向分别存储一次那么遍历每一个点求出的最大匹配数是两个完全相同的二分图的匹配数之和,/2即可;还有其他存储方式,比如vector的邻接表,或二维数组的邻接矩阵都可以)
判断与该左端结点B相连的右端节点C是否已是匹配点(是否已加入匹配),如果未加入则加入匹配,即做标记link[该右端节点]=左端结点,返回TRUE并使当前最大匹配数+1;或者如果该右端节点C已经存在另一个左端的节点A与之相连(该右端节点已是匹配点),则判断该该右端节点本来与之相连的左端节点是否在右端还有另外的节点可以与之相连,如果右端有相连的未匹配点D则将D加入匹配或者如果有相连的已匹配点则继续递归,如果找到符合要求的点则对这一路上更改过的点的link标记做更改,最后返回TRUE并使当前最大匹配数+1。
继续遍历,直到左端所有的点都已遍历一遍或当前最大匹配数已经达到最大(已是完美匹配)则结束。
③其他参考博客:匈牙利算法的BFS和DFS两种思想点击打开链接;匈牙利算法的多种写法点击打开链接;匈牙利算法的理解点击打开链接。
1)
//第一次超时,是因为有些数组或者变量没有及时初始化,因为做了个链表进行边到边的存储,所以不及时初始化可能导致某些地方成了循环 #include <iostream> #include <string.h> #include <stdio.h> #include <queue> using namespace std; const int maxn=210*2; const int maxm=210*210; int head[maxn]; int used[maxn]; int link[maxm];//匈牙利算法时,判断该点是否已是匹配点 int color[maxn]; int cur=1; //int spot=0; struct Edge{ int from; int to; int next; }edges[maxm]; void Add(int f,int t){ edges[cur].from=f; edges[cur].to=t; edges[cur].next=head[f]; head[f]=cur; cur++; } int Color(int x){//BFS染色 判断是不是二分图 queue <int> Q; Q.push(x); used[x]=1; color[x]=1; while(!Q.empty()){ int v_cur=Q.front(); Q.pop(); //int color_cur=1; for(int e_cur=head[v_cur];e_cur!=-1;e_cur=edges[e_cur].next){ int v_to=edges[e_cur].to; if(used[v_to]==0){ color[v_to]=1-color[v_cur];//总是让相邻两个点的color一个为1一个为0 used[v_to]=1; Q.push(v_to); } else if(used[v_to]==1){ if(color[v_to]==color[v_cur]){ return 1;//相邻两点,颜色相同 } } } } return 0; } int Dfs_Match(int x){//DFS匈牙利算法 求最大匹配 for(int e_cur=head[x];e_cur!=-1;e_cur=edges[e_cur].next){ int v_to=edges[e_cur].to; if(used[v_to]==0){ used[v_to]=1; if(link[v_to]==0||Dfs_Match(link[v_to])){//利用了||判断时,前者TRUE就不再进行对后者的判断,的特性 link[v_to]=x; return 1; } } } return 0; } int main() { int n,m; while(~scanf("%d%d",&n,&m)){ int x,y; cur=1; memset(edges,0,sizeof(edges)); //建图: memset(head,-1,sizeof(head)); for(int i=1;i<=m;i++){ scanf("%d%d",&x,&y); Add(x,y); Add(y,x); } //染色法判断是不是二分图 memset(used,0,sizeof(used)); memset(color,0,sizeof(color)); //如果有多个连通图,这样的处理方法只能判断第一个连通图染色情况 if(Color(1)){ cout<<"No"<<endl; continue; } //求最大匹配边数 memset(link,0,sizeof(link)); int sum=0; for(int i=1;i<=n;i++){ memset(used,0,sizeof(used)); if(Dfs_Match(i)){ sum++; } } cout<<sum/2<<endl;//因为每条边按方向不同都存储了两边,相当于求了两个完全相同的二分图的最大匹配数,所以/2 } return 0; }
2)
Description
Input
Output
Sample Input
4 4 1 2 1 3 1 4 2 3 6 5 1 2 1 3 1 4 2 5 3 6
Sample Output
No 3