<一>基本概念:
一、什么是二分图:
算法竞赛中定义:首先是一张N个点的无向图,其次这N个点可以分成A、B两个部分,
A交B为空集,且同一集合中的点没有边相连(不同集合中的两个点之间可能有边相连也
可能没有边相连,可能一条也可能多条)。
离散中定义(对偶图):任取一条边,该边的两个端点一定处于两个不同的集合中。
二、判断某一无向图是不是二分图。
1、原理:二分图中所有回路的路径长度均为偶数,即不存在长度为奇数的回路。
2、思路:黑白点标记判断标记是否会出现矛盾。
3、实现代码:
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include<algorithm> #include <set> #include <queue> #include <stack> #include<vector> #include<map> #include<ctime> #define ll long long using namespace std; const int N=10010; const int M=10010*4; int Next[M]; int ver[M]; int head[N]; int tot; void add(int x,int y) { ver[++tot]=y; Next[tot]=head[x]; head[x]=tot; } int color[N]; bool dfs(int x,int c) { color[x]=c; for(int i=head[x];i;i=Next[i]) { int y=ver[i]; if(color[y]==c)return 0; if(!color[y]&&dfs(y,-c)==false)return 0;//不用担心往回跑的情况 } return 1; } int main() { int n,m; while(cin>>n>>m) { memset(head,0,sizeof(head)); memset(ver,0,sizeof(ver)); memset(Next,0,sizeof(Next)); tot=0; memset(color,0,sizeof(color)); for(int i=1;i<=m;++i) { int x,y; cin>>x>>y; add(x,y); add(y,x); } int ans=1; for(int i=1;i<=n;++i) { if(color[i]==0&&!dfs(i,1)) { ans=0; break; } } cout<<ans<<endl; } return 0; }
4、例题:codevs1069 关押罪犯 http://codevs.cn/problem/1069/
题解:https://blog.csdn.net/qq_41661919/article/details/86422452
<二>二分图最大匹配(边数最多的一组匹配)
一、增广路概念及性质:
1、长度一定为奇数;
2、奇数边为非匹配边,偶数号边为匹配边;
3、原图中匹配状态取反后的图仍为匹配图,且最大匹配数+1;
二、匈牙利算法求最大匹配值:
1、原理:不断寻找增广路,匹配数相应+1,详细较复杂,可以再看看书,或者博客。
个人理解的话就是看每个左部节点能不能找到它的匹配节点,
通过增广路算法得到的已经匹配的点不会变为非匹配节点,顶多更
换匹配对象。能找到匹配数+1,找不到不变。
2、算法代码实现:
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include<algorithm> #include <set> #include <queue> #include <stack> #include<vector> #include<map> #include<ctime> #define ll long long using namespace std; const int N=20010; const int M=100001*2; int Next[M]; int ver[M]; int head[N]; int tot; void add(int x,int y) { ver[++tot]=y; Next[tot]=head[x]; head[x]=tot; } bool visit[N]; int match[N]; bool dfs(int x) { for(int i=head[x];i;i=Next[i]) { int y=ver[i]; if(!visit[y]) { visit[y]=1; if(!match[y]||dfs(match[y]))//对立点没有被使用,或者占用它的左节点可以 { match[y]=x; return 1; } } } return 0; } int main() { int n,m; while(cin>>n>>m) { memset(head,0,sizeof(head)); memset(Next,0,sizeof(Next)); memset(ver,0,sizeof(ver)); tot=0; memset(match,0,sizeof(match)); for(int i=1;i<=m;++i) { int x,y; cin>>x>>y; add(x,y); add(y,x); } int ans=0; for(int i=1;i<=n;++i)//遍历每一个左部节点(不好确定谁是左部节点的时候可以遍历所有节点,然后ans/2),看能不能找到其在右边的对应匹配节点 { memset(visit,0,sizeof(visit));//visit只是一个临时标记,只在单一节点为起点的搜索中起作用,搜一个节点更新一次; if(dfs(i))ans++; } cout<<ans/2<<endl; } return 0; }
3、
例题1:TYVJ棋盘覆盖
题解:https://blog.csdn.net/qq_41661919/article/details/86429537
例题2:車的放置 蓝书
题意:
N*M的棋盘,某些个子进制放置棋子,问棋盘上最多能防止多少个不能相互攻击的車。N,M<=200。
分析:
1要素:1行只能有一个棋子,一列同样只能有一个棋子。棋子可以同时占某一行和某一列。
0要素:不同行或者不同列之间不会同时有同一个棋子。
最终,所以左部节点为行号,右部节点为列号,连线表示棋子,建立二分图。
三、完备匹配概念。
二分图左右部节点数相同,且二分图最大匹配数==n。
四、二分图最小点覆盖。
1、定义:
二分图中一最小点集S,使得从二分图中任取一条边,
该边至少以一个端点位于S中。即能够覆盖所有边的最小S集。
2、定理:二分图最小覆盖点包含的点数等于二分图最大匹配包含的边数。
例题:POJ 1325 Machine Schedule
题解:https://blog.csdn.net/qq_41661919/article/details/86444389
五、二分图最大独立集:
1、定义:任意两点之间没有边相连的最大点集。最大团:任意两点之间均有边相连的最大点集。
2、定理:二分图的最大独立集中点的个数等于n(左+右)-二分图的最大匹配。
定理:二分图的最大团中点的个数等于二分图补图的最大匹配。
六、有向无环图的最小不相交路径点覆盖:
七、有向无环图的最小可相交路径点覆盖: