最小顶点覆盖==二分图的最大匹配
最少路径覆盖==n—最大匹配////n为左右集合中的顶点个数
最大点独立集 = 顶点共个数 - 匹配顶点数
多个环的并
例如:
有n个国家,国家之间通过有向边相连,边有权值,
现在让你把所有的国家都划成一个一个的圈,使得所有圈的总权值和最大。
二分图的实质就是多个环,该题就是直接求出的二分图的最大权匹配就能求出最终结果。
二分图的多个环的并就是二分图的最大(小)权匹配。
所以KM求得最大匹配就是结果
////////
下面给出关于二分图最大匹配的两个定理
1:最大匹配数 + 最大独立集 = n + m
2:二分图的最小覆盖数 = 最大匹配数
3:最小路径覆盖 = 最大独立集
最大独立集是指求一个二分图中最大的一个点集,该点集内的点互不相连。
最小顶点覆盖是指 在二分图中,用最少的点,让所有的边至少和一个点有关联。
最小路径覆盖是指一个不含圈的有向图G中,G的一个路径覆盖是一个其结点不相交的 路径集合P,图中的每一个结点仅包含于P中的某一条路径。路径可以从任意结点
开始和结束,且长度也为任意值,包括0
二分图的多重匹配
二分图的多重匹配 const int maxn = 100005; const int maxm = 15; int cap[maxm]; int Link[maxm][maxn]; int vLink[maxm]; int mat[maxn][maxm]; int vis[maxm]; int n, m; bool Find(int u) { for(int i = 1; i <= m; i++) { if(!vis[i] && mat[u][i]) { int v = i; vis[v] = 1; if(vLink[v] < cap[v]) { Link[v][vLink[v]++] = u; return true; } for(int j = 0; j < vLink[v]; j++) { if(Find(Link[v][j])) { Link[v][j] = u; return true; } } } } return false; } bool solve() { memset(Link, 0, sizeof(Link)); memset(vLink, 0, sizeof(vLink)); for(int i = 1; i <= n; i++) { memset(vis, 0, sizeof(vis)); if(!Find(i)) return false; } return true; } int main() { while(EOF != scanf("%d %d",&n, &m)) { for(int i = 1; i <= n; i++) { for(int j = 1; j <= m; j++) { scanf("%d",&mat[i][j]); } } for(int i = 1; i <= m; i++) { scanf("%d",&cap[i]); } if(solve()) puts("YES"); else puts("NO"); } return 0; }
二分图最大匹配模板
#include<stdio.h> #include<string.h> const int maxn = 105; const int INF = 1000000000; bool vis[maxn]; //查询右集合中的点有没有被访问过 int link[maxn]; //link[i]表示右集合中的i点是由左集合中的哪个点连接的 int G[maxn][maxn]; //邻接矩阵 int x_cnt; int y_cnt; //左右集合的点的个数 bool find(int u) //用来寻找增广路 { for(int i = 1; i <= y_cnt; i++) //遍历右集合中的每个点 { if(!vis[i] && G[u][i]) //没有被访问过并且和u点有边相连 { vis[i] = true; //标记该点 if(link[i] == -1 || find(link[i])) { //该点是增广路的末端或者是通过这个点可以找到一条增广路 link[i] = u;//更新增广路 奇偶倒置 return true;//表示找到一条增广路 } } } return false;//如果查找了右集合里的所有点还没找到通过该点出发的增广路,该点变不存在增广路 } int solve() { int num = 0; memset(link, -1, sizeof(link));//初始化为-1表示 不与左集合中的任何元素有link for(int i = 1; i <= x_cnt; i++) //遍历左集合 { memset(vis, false, sizeof(vis));//每一次都需要清除标记 if(find(i)) num++;//找到一条增广路便num++ } return num; } int main() { while(scanf("%d%d",&x_cnt,&y_cnt)!=EOF){ memset(G,0,sizeof(G)); int n; int x,y; scanf("%d",&n); for(int i=1;i<=n;i++) {scanf("%d%d",&x,&y); G[x][y]=1; } printf("%d ",solve()); } return 0; }