UOJ79 一般图最大匹配

题目描述

从前一个和谐的班级,所有人都是搞OI的。有 nn 个是男生,有 00 个是女生。男生编号分别为 1,…,n1,…,n。

现在老师想把他们分成若干个两人小组写动态仙人掌,一个人负责搬砖另一个人负责吐槽。每个人至多属于一个小组。

有若干个这样的条件:第 vv 个男生和第 uu 个男生愿意组成小组。

请问这个班级里最多产生多少个小组?

输入格式

第一行两个正整数,n,mn,m。保证 n≥2n≥2。

接下来 mm 行,每行两个整数 v,uv,u 表示第 vv 个男生和第 uu 个男生愿意组成小组。保证 1≤v,u≤n1≤v,u≤n,保证 v≠uv≠u,保证同一个条件不会出现两次。

输出格式

第一行一个整数,表示最多产生多少个小组。

接下来一行 nn 个整数,描述一组最优方案。第 vv 个整数表示 vv 号男生所在小组的另一个男生的编号。如果 vv 号男生没有小组请输出 00。

样例一

input

10 20
9 2
7 6
10 8
3 9
1 10
7 1
10 9
8 6
8 2
8 1
3 1
7 5
4 7
5 9
7 8
10 4
9 1
4 8
6 3
2 5

output

5
9 5 6 10 2 3 8 7 1 4

样例二

input

5 4
1 5
4 2
2 1
4 3

output

2
2 1 4 3 0

正解:带花树算法

解题报告:

  这道题是一般图最大匹配,也就是带花树算法裸题。

  大概讲一下一般图最大匹配的思想:一般图最大匹配由带花树算法实现,2015年国家集训队论文中我校学长陈胤伯详细介绍了这一算法。

  考虑一般图与二分图最大的不同就在于一般图存在奇环,所以我们不能完全套用二分图最大匹配的算法。

  在这里不加以证明的给出具体做法(证明详见2015年国家队论文):

  每次从一个未盖点出发,将其命名为偶点(偶点匹配的点称之为奇点),枚举其出边以及出边连接的点v:

  如果v在本次BFS中还未被经过,则假设未匹配,那么找到了一条增广路,原路返回,把原来的匹配边和非匹配边取反,这样可以使答案加一;    否则,将v的匹配点加入队列中拓展,根据我们上面的定义,v的匹配点显然也是偶点。

  如果v已经访问过,那么显然出现了环,这个环如果是偶环则不用考虑,如果是奇环且本身两个点就不处在同一个现有已经处理过的奇环中,我们就需要将其缩为一个点(或者说是一朵花),并且把上面的奇点全都标记为偶点,丢到队列里面拓展。

  这就是带花树的完整做法。不理解的可以结合我的代码理解一下。我就是看别人代码看懂的......

  代码如下:

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 520;
const int MAXM = 250011;
const int MAXL = 10011;
int n,m,ecnt,first[MAXN],next[MAXM],to[MAXM],father[MAXN],Tim;
int dui[MAXL],head,tail,id[MAXN],pre[MAXN],match[MAXN],ans,vis[MAXN];
inline int find(int x){ if(father[x]!=x) father[x]=find(father[x]); return father[x]; }
inline int getint(){
    int w=0,q=0; char c=getchar(); while((c<‘0‘||c>‘9‘) && c!=‘-‘) c=getchar();
    if(c==‘-‘) q=1,c=getchar(); while (c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar(); return q?-w:w;
}

inline int lca(int x,int y){
	Tim++;
	while(vis[x]!=Tim) {
		if(x) {
			x=find(x);
			if(vis[x]==Tim) return x;
			vis[x]=Tim;
			if(match[x]!=0)	x=find(pre[match[x]]);
			else x=0;
		}
		swap(x,y);
	}
	return x;
}

inline void change(int x,int y,int k){//把奇环上的点缩成一个点,并且把原来是奇点的点变成偶点,加入队列
	while(find(x)!=k) {
		pre[x]=y; int z=match[x];
		if(id[z]==1) { id[z]=0; dui[++tail]=z; }
		if(find(z)==z) father[z]=k;
		if(find(x)==x) father[x]=k;
		y=z; x=pre[y];
	}
}

inline bool bfs(int ini){
	for(int i=1;i<=n;i++) id[i]=-1,father[i]=i;
	head=tail=0; dui[++tail]=ini; id[ini]=0; int u;
	while(head<tail) {
		u=dui[++head];
		for(int i=first[u];i;i=next[i]) {
			int v=to[i];
			if(id[v]==-1) {
				pre[v]=u; id[v]=1;
				if(!match[v]) {
					int last,t,now=v;
					while(now!=0) {
						t=pre[now]; last=match[t];
						match[t]=now; match[now]=t;
						now=last;
					}
					return true;
				}
				id[match[v]]=0; dui[++tail]=match[v];
			}
			else if(id[v]==0&&find(u)!=find(v)){ //出现奇环且不是在同一个环中
				int g=lca(u,v);
				change(u,v,g);
				change(v,u,g);
			}
		}
	}
	return false;
}

inline void work(){
	n=getint(); m=getint(); int x,y;
	for(int i=1;i<=m;i++) {
		x=getint(); y=getint();
		next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y;
		next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x;
	}
	for(int i=1;i<=n;i++) if(!match[i]&&bfs(i)) ans++;
	printf("%d\n",ans);
	for(int i=1;i<=n;i++) printf("%d ",match[i]);
}

int main()
{
    work();
    return 0;
}

  

  

时间: 2024-10-15 02:23:37

UOJ79 一般图最大匹配的相关文章

【题解】Uoj79一般图最大匹配

带花树裸题,感觉带花树强强……不会的勿看此文,解释的可能不对,只是给自己看的!!!如题,带花树即为求一般图最大匹配算法(匈牙利与dinic为二分图最大匹配).推荐论文:2015年<浅谈图的匹配算法及其应用>(长郡中学    ——陈胤伯).论文当中有对于带花树算法的详细解析,在这里只想记录一下算法的基本流程: —————————————————————————— \(id\) : 记录一个点为奇点/偶点(0偶1奇). \(fa\) : 并查集记录一个点属于哪一个点为根的花. ——————————

HDOJ 4687 Boke and Tsukkomi 一般图最大匹配带花树+暴力

一般图最大匹配带花树+暴力: 先算最大匹配 C1 在枚举每一条边,去掉和这条边两个端点有关的边.....再跑Edmonds得到匹配C2 如果C2+2==C1则这条边再某个最大匹配中 Boke and Tsukkomi Time Limit: 3000/3000 MS (Java/Others)    Memory Limit: 102400/102400 K (Java/Others) Total Submission(s): 649    Accepted Submission(s): 202

最短路思想实现一般图最大匹配

考虑一下一般图和二分图的区别,无非就在于二分图可能出现长为奇数的环. 如何排除奇数环的影响??? 最短路 设一般图中每条可匹配边长为1,该次匹配起点为v. 根据最短路定理,我们可以知道,v在带花树上到任意一个终点(即未匹配的点)的最短距离一定为奇数,且路径上一定不存在环. 那么,用广搜的方法去贪最短路的时候,贪出来的第一条路(即最短路)一定不含环. 设[1,n]代表S型点,[n+1,2*n]代表T型点,dist[i]表示v到i的最短路上经过的点数. 设ance[i][j]表示i的dist值为j的

poj 3020 一般图最大匹配 带花树开花算法

题意: 给出一个h*w的图,每个点都是'o'或'*',最少要用多少个1*2的矩形才能把图中所有的'*'都覆盖掉. 限制: 1 <= h <= 40; 1 <= w <= 10 思路: 最小边覆盖=|V|-最大匹配 一般图最大匹配,带花树开花算法 /*poj 3020 一般图最大匹配 带花树开花算法 题意: 给出一个h*w的图,每个点都是'o'或'*',最少要用多少个1*2的矩形才能把图中所有的'*'都覆盖掉. 限制: 1 <= h <= 40; 1 <= w &l

ZOJ 3316 Game 一般图最大匹配带花树

一般图最大匹配带花树: 建图后,计算最大匹配数. 如果有一个联通块不是完美匹配,先手就可以走那个没被匹配到的点,后手不论怎么走,都必然走到一个被匹配的点上,先手就可以顺着这个交错路走下去,最后一定是后手没有路可走,因为如果还有路可走,这一条交错路,就是一个增广路,必然有更大的匹配. Game Time Limit: 1 Second      Memory Limit: 32768 KB Fire and Lam are addicted to the game of Go recently.

UOJ #79 一般图最大匹配

一般图最大匹配 从前一个和谐的班级,所有人都是搞OI的.有 \(n\) 个是男生,有 \(0\) 个是女生.男生编号分别为 \(1,-,n\). 现在老师想把他们分成若干个两人小组写动态仙人掌,一个人负责搬砖另一个人负责吐槽.每个人至多属于一个小组. 有若干个这样的条件:第 \(v\) 个男生和第 \(u\) 个男生愿意组成小组. 请问这个班级里最多产生多少个小组? 输入格式 第一行两个正整数,\(n,m\).保证 \(n≥2\). 接下来 \(m\) 行,每行两个整数 \(v,u\) 表示第

带花树——一般图最大匹配

问题 给定一个图,求该图的最大匹配.即找到最多的边,使得每个点至多属于一条边. 这个问题的退化版本就是二分图最大匹配. 由于二分图中不存在奇环,偶环对最大匹配并无影响(可以调整).所以增广路算法是可以顺利应用的. 在一般图中,我们还是尝试使用BFS增广路的算法. 然而一般图中还会出现奇环,在寻找增广路的时候,怎么处理奇环上的冲突? 目的就是将奇环不断地缩起来(缩花),使得整个图在使用增广算法的时候不受影响,即不会经过奇环. ? ? ? 花 ? 一朵花由一个奇环缩点而成,一朵花里面可能还会有花.

一般图最大匹配带花树

参考博客:http://blog.sina.com.cn/s/blog_95ec9e7401018bga.html https://www.cnblogs.com/owenyu/p/6858508.html 用Dinic实现的二分图匹配的时间复杂度其实是O(M*N^0.5),这也许能够解释为什么一般网络流算法比Hungry要快了. 另外,带花树算法的正确性的证明比较困难:而其时间复杂度是可以做到O(M*N^0.5)的 简述一下“带花树”算法吧: 它的核心思想还是找增广路.假设已经匹配好了一堆点,

Work Scheduling URAL - 1099 一般图最大匹配带花树

#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <string> using namespace std; const int MAXN = 250; int N; //点的个数,点的编号从1到N bool Graph[MAXN][MAXN]; int Match[MAXN]; bool InQueue[MAXN],I