一般图的最大匹配问题(真心觉得难)

来源:互联网 发布:许知远 陈嘉映 知乎 编辑:程序博客网 时间:2024/05/01 18:05

参考博客:http://blog.csdn.net/yihuikang/article/details/10460997

论文地址:http://builtinclz.abcz8.com/art/2012/Galil%20Zvi.pdf 

概念分析:

未盖点:设Vi是 图G 的一个顶点,如果Vi 不与任意一条(属于匹配M)的边相关联,就称Vi 是一个未盖点。简言之,点不在匹配边的两端。
交错路:设P是 图G 的一条路,如果P的任意两条相邻的边一定是一条属于M而另一条不属于M,就称P是一条交错路。简言之,一条匹配边,一条非匹配边,交错。
可增广路:(两个端点都是未盖点的)交错路叫做可增广路。简言之,就是非匹配-匹配-非匹配 的样式。

基本定义:
图的定义:G(V,E),参考原文 点的数目定义为 |V|=m,|E|=n。
M    匹配边的集合,就这些匹配边而言,没有任何两条边相交的(或者说共有一个结点)。比如说1、2、3、4四个节点,它的一组匹配可能是(1,2)、(3,4)。

问题定义:

1. 二分图最大匹配

2. 一般图最大匹配

3. 二分图最大权匹配

4. 一般图最大权匹配


希望在这篇文档里面能基本解决第2个问题,而应具体需要,下一步解决第四个问题,以便论文的需要。

一般图的最大匹配要用到带花树算法(blossom),带花树的原理如下:

所谓的带花树算法就是,把整个圈缩成一个点,Edmonds称这个超级点为“花”,

就是说,圆圈里的所有点都作为外点,然后继续搜索。再之后的过程中,已经被缩的点还可能被嵌套地收缩。(外点和内点相区别)

当我们找到一条 增广路之后,还要把路上的“花”展开。

 总之,带花树的算法思想就是缩点-继续找增广路-找到之后把花展开



实现的要点:(参考他人博客)
首先,我们不“显式”地表示花。
我们记录一个Next数组,表示最终增广的时候的路径上的后继。
同时,我们维护一个并查集,表示每个点现在在以哪个点为根的花里(一个花被缩进另一朵花之后就不算花了)。
还要记录每个点的标记。

主程序是一段BFS。对于每个由x发展出来的点y,分4种情况讨论:
1、x y是配偶(不说夫妻,这是非二分图。。。)或者xy现在是一个点(在一朵花里):直接无视
2、y是T型点:直接无视
3、y目前单身:进行增广
4、y是一个S型点:缩点(个人理解,按论文所说,y一共有三种情况T、S、free

缩点的时候要进行的工作:(上图的过程)
1、找x和y的LCA(即最近的公共根)p。找LCA可以用各种方法,直接朴素也行。
2、在Next数组中把x和y接起来(表示它们形成环了!)
3、从x、y分别走到p,修改并查集使得它们都变成一家人,同时沿路把Next数组接起来。

基本理论基本结束,下面是具体实现,很难奋斗
参考博客中的代码虽然好(使用邻接矩阵),但在处理大规模数据的时候(比如说5W个点),空间复杂度太高。所以我改用邻接表进行运算,部分代码如下:

/*------------------------------新建图的信息-------------------*/int Blossom::CreateADG(){/*v1、v2是一条边的两个顶点*/FILE *fp;ArcNode *q,*p;int i,j,v1,v2;/*图的顶点、关联边的数目*/if((fp=fopen("bunny_output1.smf","rb"))==NULL){printf("can not open file\n");exit(0);}fscanf(fp,"%d %d\n", &myGraph.vexnum,&myGraph.arcnum);std::cout<<"graph vex number:";std::cout<<myGraph.vexnum<<std::endl;std::cout<<"graph arc number:";std::cout<<myGraph.arcnum<<std::endl; XfN::alloc1T(cha,myGraph.arcnum);/*输入头结点*/if(myGraph.vertices=(VNode*)malloc(sizeof(VNode)*myGraph.vexnum)){for(i=0;i<myGraph.vexnum;i++){myGraph.vertices[i].data = i;myGraph.vertices[i].firstarc = NULL;}}else{std::cout<<"malloc fail!"<<std::endl;return 0;}/*输入边的信息*/for(j=0; j<myGraph.arcnum; j++){fscanf(fp,"%c %d %d\n",&cha[j], &v1, &v2);//\n很重要if(q=(ArcNode*)malloc(sizeof(ArcNode))){q->adjvex = v2;q->nextarc = NULL;if(myGraph.vertices[v1].firstarc==NULL)myGraph.vertices[v1].firstarc = q;else{ArcNode* q1 = myGraph.vertices[v1].firstarc;while(q1->nextarc)q1 = q1->nextarc;q1->nextarc=q;}/*//头插法q->nextarc = myGraph.vertices[v1].firstarc;myGraph.vertices[v1].firstarc = q;*/}if(p=(ArcNode*)malloc(sizeof(ArcNode))){p->adjvex = v1;p->nextarc = NULL;if(myGraph.vertices[v2].firstarc==NULL)myGraph.vertices[v2].firstarc = p;else{q = myGraph.vertices[v2].firstarc;while(q->nextarc)q = q->nextarc;q->nextarc=p;}/*p->nextarc = myGraph.vertices[v2].firstarc;myGraph.vertices[v2].firstarc = p;*/}else{std::cout<<"malloc arc fail!"<<std::endl;return 0;}}return 1;}

/*----------------------------带花树算法-------------------*/int* belong;int* match;int* next;int* mark;int* vis;int* Q;int rear;ArcNode *q;void initFactor(Blossom& blo) {belong = (int*)malloc(blo.myGraph.vexnum * sizeof(int));match = (int*)malloc(blo.myGraph.vexnum * sizeof(int));next = (int*)malloc(blo.myGraph.vexnum * sizeof(int));mark = (int*)malloc(blo.myGraph.vexnum * sizeof(int));vis = (int*)malloc(blo.myGraph.vexnum * sizeof(int));Q = (int*)malloc(blo.myGraph.vexnum * sizeof(int));}

/*---并查集---*/(参考原文)
/*---朴素算法求某阶段中搜索树上两点x, y的最近公共祖先r(可优化)---*/ 
/*---增广---*/

void Blossom::solve(){FILE *fp;if((fp=fopen("bunny_output2.smf","w+"))==NULL){printf("can not open file\n");exit(0);}int i,tot = 0;initFactor(*this);for(i = 0 ; i < myGraph.vexnum ; i++)match[i]=-1;for(i = 0 ; i < myGraph.vexnum ; i++)if(match[i]==-1) aug(*this,i);for (i = 0 ; i < myGraph.vexnum; i++) if (match[i] != -1) tot++;printf("%d\n", tot/2);for (i = 0; i < myGraph.vexnum; i++) if (match[i] > i){printf("%d %d\n", i, match[i]);}fclose(fp);}