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

参考博客: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);

}








  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值