标签:
考虑一下一般图和二分图的区别,无非就在于二分图可能出现长为奇数的环。
如何排除奇数环的影响???
最短路
设一般图中每条可匹配边长为1,该次匹配起点为v。
根据最短路定理,我们可以知道,v在带花树上到任意一个终点(即未匹配的点)的最短距离一定为奇数,且路径上一定不存在环。
那么,用广搜的方法去贪最短路的时候,贪出来的第一条路(即最短路)一定不含环。
设[1,n]代表S型点,[n+1,2*n]代表T型点,dist[i]表示v到i的最短路上经过的点数。
设ance[i][j]表示i的dist值为j的祖先的原编号,hash[i][j]表示j(原编号)是否是i的祖先。
那么i能拓展出j的充要条件就是(dist[i]+1<dist[j]&&!hash[i][j])
即满足最短路且满足非祖先关系。
通过广搜实现这个特殊图的最短路,从而得到一般图最大匹配。
代码如下,带上注释比我从网上找到的最短的带花树(不带注释)要短200字节
#include<cstdio>//O(n^3),神板子,码量不大 #include<algorithm> #include<cstring> #include<vector> using namespace std; const int maxl=99999999; const int maxn=510; int n,m,i,j,s,v,match[maxn],ance[maxn*2][maxn],dist[maxn*2],ans; //[1,n]代表S型点,[n+1,2*n]代表T型点 //ance[i][j]表示i的dist为j的祖先的原编号,hash[i][j]表示j(原编号)是否是i的祖先 vector <int> l[maxn]; bool hash[maxn*2][maxn]; int z[maxn*2],r; void change(int x){ ans++; for (int i=dist[x];i>0;i-=2){ match[ance[x][i]]=ance[x][i-1]; match[ance[x][i-1]]=ance[x][i]; } } void extend(int x){ int i,j,k,v=(x>n?x-n:x); if (x>n){//若为T型点 if (match[v]==0){//发现单身,增广 change(x);return; } k=match[v]; if (!hash[x][k]&&dist[k]>dist[x]+1){//十分类似于dijsktra的松弛操作 dist[k]=dist[x]+1; for (j=1;j<=dist[x];j++) hash[k][ance[k][j]=ance[x][j]]=1; hash[k][ance[k][dist[k]]=k]=1; z[++r]=k; } } else for (i=0;i<(int)l[v].size();i++){//十分类似于dijsktra的松弛操作 k=n+l[v][i]; if (!hash[x][l[v][i]]&&dist[k]>dist[x]+1){ dist[k]=dist[x]+1; for (j=1;j<=dist[x];j++) hash[k][ance[k][j]=ance[x][j]]=1; hash[k][ance[k][dist[k]]=l[v][i]]=1; z[++r]=k; } } } void bfs(int x){ z[r=1]=x; //初始化 memset(ance,0,sizeof ance); memset(hash,0,sizeof hash); for (int i=1;i<=2*n;i++) dist[i]=maxl; dist[x]=hash[x][x]=1; ance[x][1]=x; for (int i=1;i<=r;i++){ //广搜 extend(z[i]); if (match[x]!=0) return; } } int main() { scanf("%d%d",&n,&m); for (i=1;i<=m;i++){ scanf("%d%d",&s,&v); l[s].push_back(v); l[v].push_back(s); } for (i=1;i<=n;i++) if (match[i]==0) bfs(i); printf("%d\n",ans); for (i=1;i<=n;i++) printf("%d ",match[i]); putchar(‘\n‘); return 0; }
标签:
原文地址:http://www.cnblogs.com/FoolMike/p/5503387.html