标签:
考虑一下一般图和二分图的区别,无非就在于二分图可能出现长为奇数的环。
如何排除奇数环的影响???
最短路
设一般图中每条可匹配边长为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