一般图最大匹配(UOJ-79)
Problem Description
从前一个和谐的班级,所有人都是搞OI的。有 n 个是男生,有 0 个是女生。男生编号分别为 1,…,n。
现在老师想把他们分成若干个两人小组写动态仙人掌,一个人负责搬砖另一个人负责吐槽。每个人至多属于一个小组。
有若干个这样的条件:第 v 个男生和第 u 个男生愿意组成小组。
请问这个班级里最多产生多少个小组?
Input
第一行两个正整数,n,m。保证 n≥2。
接下来 m 行,每行两个整数 v,u 表示第 v 个男生和第 u 个男生愿意组成小组。保证 1≤v,u≤n,保证 v≠u,保证同一个条件不会出现两次。
Output
第一行一个整数,表示最多产生多少个小组。
接下来一行 n 个整数,描述一组最优方案。第 v 个整数表示 v 号男生所在小组的另一个男生的编号。如果 v 号男生没有小组请输出 0。
Examples
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 5Output
5
9 5 6 10 2 3 8 7 1 4Input
5 4
1 5
4 2
2 1
4 3Output
2
2 1 4 3 0
思路:
题目本质是给出 n 个点 m 条边的无向图,要求一个最大匹配并输出任意一种方案
对于一般图的匹配,使用带花树算法即可,本题是一个带花树裸题
Source Program
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<bitset>
#define EPS 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
#define Pair pair<int,int>
const int MOD = 1073741824;
const int N = 3000+5;
const int dx[] = {-1,1,0,0,-1,-1,1,1};
const int dy[] = {0,0,-1,1,-1,1,-1,1};
using namespace std;
struct Edge {
int to,next;
} edge[N*N*2];
int head[N],tot;
int n;//n个点
int father[N],pre[N];//father记录一个点属于哪个一个点为根的花
int Q[N*N*2],first,tail;//bfs队列
int match[N];//匹配
bool odd[N],vis[N];//odd记录一个点为奇点/偶点,1为奇,0为偶
int timeBlock;//LCA时间戳
int top[N],rinedge[N];
void addEdge(int x,int y) {//添边
edge[tot].to=y;
edge[tot].next=head[x];
head[x]=tot++;
}
int Find(int x){//并查集寻找根节点
if(father[x]!=x)
return father[x]=Find(father[x]);
return x;
}
int lca(int x, int y){//求解最近公共祖先
timeBlock++;
while(x){
rinedge[x]=timeBlock;
x=Find(top[x]);
}
x=y;
while(rinedge[x]!=timeBlock)
x=Find(top[x]);
return x;
}
void blossom(int x, int y, int k) {//将奇环缩成一个点并将原来是奇点的点变为偶点并加入队列
while(Find(x)!=Find(k)){
pre[x]=y;
y=match[x];
odd[y]=false;
Q[tail++]=y;
father[Find(x)]=k;
father[Find(y)]=k;
x=pre[y];
}
}
bool bfs(int s) {
memset(top,0,sizeof(top));
memset(pre,0,sizeof(pre));
memset(odd,false,sizeof(odd));
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++)
father[i]=i;
vis[s]=true;
first=tail=0;
Q[tail++]=s;
while(first!=tail){
int now=Q[first++];
for(int i=head[now];i!=-1;i=edge[i].next){
int to=edge[i].to;
if(!vis[to]){
top[to]=now;
pre[to]=now;
odd[to]=true;
vis[to]=true;
if(!match[to]){
int j=to;
while(j){
int x=pre[j];
int y=match[x];
match[j]=x;
match[x]=j;
j=y;
}
return true;
}
vis[match[to]]=true;
top[match[to]]=to;
Q[tail++]=match[to];
}
else if(Find(now)!=Find(to) && odd[to]==false) {
int k=lca(now,to);
blossom(now,to,k);
blossom(to,now,k);
}
}
}
return false;
}
int main() {
memset(head,-1,sizeof(head));
memset(match,0,sizeof(match));
tot=0;
int m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
addEdge(x,y);
addEdge(y,x);
}
int res=0;
for(int i=1;i<=n;i++)
if(!match[i])
res+=bfs(i);
printf("%d\n",res);
for(int i=1;i<=n;i++)
printf("%d ",match[i]);
printf("\n");
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】