二分图匹配问题:
做法:
①匈牙利算法,时间复杂度O(N*V)
②Hopcroft-Karp,时间复杂度O(√N*V)
相关结论:
①最小顶点覆盖(könig定理)
二分图的最小顶点覆盖=最大匹配数
②最小路径覆盖(不要求二分图):在图中找一些路径,使之覆盖了图中的所有顶点,且任何一个顶点有且只有一条路径与之关
最小路径覆盖 = 顶点数 - 最大匹配配
对于有向无环图,首先拆点,建成二分图再进行求解
·最小不相交路径覆盖
建图方式:把一个的点V拆点成Vx和Vy,如果A连向B,那么就建一条Ax连向By的边。
图中有多少条路径,可以以一种方法得到,就是计算出度为0的点的个数。如果知道这个就很容易得出这个结论了
·最小相交路径覆盖
做法首先跑floyd,求出原图的传递闭包,然后用上述方法做即可
③最小边覆盖
最小边覆盖=图顶点-最大匹配
首先一开始,假如一条边都不选的话,要覆盖所有的点就必须每个点都选一次,也就是n次,然后每选一条边就会减少1个,所以结论显而易见
最大独立集
最大独立集=图顶点-最大匹配=最小边覆盖
二分图的独立数等于顶点数减去最大匹配数,很显然的把最大匹配两端的点都从顶点集中去掉这个时候剩余的点是独立集,这是|V|-2*|M|,同时必然可以从每条匹配边的两端取一个点加入独立集并且保持其独立集性质。
二分图多重匹配
( 一 ) 如果x部节点只对应一个y部节点,而y部节点可以对应多个x部节点,那么这种匹配可以用匈牙利算法来解决
解决的问题:一个y最多匹配cnt个x是否成立,要问一个y匹配人数最大的最小值可以用二分答案来做
解决思路:根据匈牙利算法的思想,这时的link[u]要变成link[u][i],表示与y[u]匹配好了的第i个点,用vlink[u]记录已经于u点匹配了的点的个数,对于x中的x[k],找到一个与他相连的y[i]后,同样判断匈牙利算法中的两个条件是否成立,若满足第一个条件,直接将x[k],y[i]匹配,否则,如果与y[i]所匹配的点已经达到了饱和,那么在所有与y[i]配合的点中选一个点,检查能否找到增广路,如果能,就让出位置让x[k]与y[i]匹配
( 二 )如果x部节点可以匹配多个y部节点,y部节点可以同时匹配多个x部节点,那么应该用网络流来解决。(因为匈牙利算法无法应对两边都可以选多个这种情况)
如何建图:假设x部节点的容量为capx[ i ],y部节点的容量为capy[ i ],同时给出x部节点可以与y部节点相连的的边,那么对于每个x部节点,超级源点都与x部节点连边,边权为capx[i];对于每个y部节点,都与超级汇点连接边,边权为capy[i]。然后连接每个x与y直接相连的边,边权为1。
这样一来,求出最大流就是最大匹配方案了:流量通道上的边的剩余流量代表匹配结果
带权二分图的权值最大的完备匹配
做法:KM算法,时间复杂度O(N3)
相关博客:
https://blog.csdn.net/qq_37943488/article/details/78586048
https://www.cnblogs.com/zpfbuaa/p/7218607.html
一般图匹配问题
做法:带花树算法(会套板子即可)
描述:一般图与二分图的主要差别都集中在了奇环上,所以带花树最大的目标,就是解决奇环对算法的影响。
首先,我们仍然当做是二分图来做:仍然是暴力找增广路径,对于我们枚举到的相邻点v
若v未访问过:1、若v已经匹配,则从v开始继续bfs2、若v未匹配,则找到一条增广路
若v访问过,则找到一个环:1、若为偶环,直接忽略,跳过当前节点2、若为奇环,则将当前的环暴力缩点,将环缩成一朵陈村花
HDU1045 Fire Net
题意:
在一个n*n的地图上放置炮台,每个炮台之间不能相互攻击(在同一行或一列上),如果中间放有障碍物则可以抵挡相互攻击,现在问最多能放多少个炮台
思路:
将每行中每列中被障碍物分割的区域分别进行缩点成为不同的点
将横坐标分成一组,纵坐标分成一组进行二分图最大匹配
#include<iostream> #include<algorithm> #include<vector> #include<cstring> using namespace std; const int maxn=100; char s[maxn][maxn]; int idx[maxn][maxn],idy[maxn][maxn],matching[maxn],check[maxn]; int n,cntx,cnty; vector<int> a[maxn]; bool dfs(int u) { for (int i=0;i<a[u].size();i++){ int v = a[u][i]; if (!check[v]) { check[v] = true; if (matching[v]==-1||dfs(matching[v])) { matching[v]=u; matching[u]=v; return true; } } } return false; } int hungarian(int cntx) { int ans=0; memset(matching,-1,sizeof(matching)); for (int u=0;u<=cntx;++u){ if(matching[u]==-1){ memset(check, 0, sizeof(check)); if(dfs(u)) ++ans; } } return ans; } void init() { memset(idx,0,sizeof(idx)); memset(idy,0,sizeof(idy)); memset(s,0,sizeof(s)); memset(matching,0,sizeof(matching)); for(int i=0;i<maxn;i++) a[i].clear(); cntx=cnty=0; } int main() { while(scanf("%d",&n)&&n){ init(); for(int i=0;i<n;i++) scanf("%s",s[i]); for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ if(s[i][j]=='.') idx[i][j]=cntx; else if(s[i][j]=='X'&&s[i][j+1]!='X') cntx++; } if(s[i][n-1]!='X'&&s[i+1][0]!='X'&&i!=n-1) cntx++; for(int j=0;j<n;j++){ if(s[j][i]=='.') idy[j][i]=cnty; else if(s[j][i]=='X'&&s[j+1][i]!='X') cnty++; } if(s[n-1][i]!='X'&&s[0][i+1]!='X'&&i!=n-1) cnty++; } for(int i=0;i<n;i++) for(int j=0;j<n;j++) if(s[i][j]!='X') a[idx[i][j]].push_back(idy[i][j]+cntx+1); cout<<hungarian(cntx)<<endl; } return 0; }