从前一个和谐的班级,所有人都是搞OI的。有 n 个是男生,有 0 个是女生。男生编号分别为 1,…,n 。
现在老师想把他们分成若干个两人小组写动态仙人掌,一个人负责搬砖另一个人负责吐槽。每个人至多属于一个小组。
有若干个这样的条件:第 v 个男生和第 u 个男生愿意组成小组。
请问这个班级里最多产生多少个小组?
输入格式
第一行两个正整数, n,m 。保证 n≥2 。
接下来 m 行,每行两个整数 v,u 表示第 v 个男生和第 u 个男生愿意组成小组。保证 1≤v,u≤n ,保证 v≠u ,保证同一个条件不会出现两次。
输出格式
第一行一个整数,表示最多产生多少个小组。
接下来一行 n 个整数,描述一组最优方案。第 v 个整数表示 v 号男生所在小组的另一个男生的编号。如果 v 号男生没有小组请输出 0
模板:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e3 + 5;
const int maxm = 1e6 + 5;
int n, m, edgecnt, head[maxm], f[maxn];
int match[maxn];
int id[maxn];// 这个点是什么点 -1没访问过, 0:s点,1;t点
int que[maxn];//要扩展的队列————也就是我们要尝试帮谁换配偶
int pre[maxn];//在这次过程中,x的新配偶是谁
int tim, book[maxn]; //对于lca的标记以及时间轴
struct node
{
int u, v, next;
}edge[maxm];
void addEdge(int u, int v)
{
edge[edgecnt].v = v;
edge[edgecnt].next = head[u];
head[u] = edgecnt++;
}
int Find(int x)
{
return f[x] == x ? x : f[x] = Find(f[x]);
}
int lca(int x, int y)
{
tim++;
while(book[x] != tim)
{
if(x != 0)
{
x = Find(x);
if(book[x] == tim) return x;
book[x] = tim;
if(match[x] != 0) x = Find(pre[match[x]]);
//因为在之前我们知道,每一个S点的配偶(也就是T点)的pre 都是指向他的父亲的,于是就直接这么跳就可以了
//还有要注意的是,一定要先去到花根,因为他们现在已经是一个点了,只有花根的pre才指向他们真正的父亲
else x = 0;
}
swap(x, y);
}
return x;
}
int st, ed;
void change(int x, int y, int k)//环 出现的是x---y的连边 已知根是k
{
while(Find(x) != k)
{
pre[x] = y;
int z = match[x];
id[z] = 0;
que[ed++] = z;
// if(ed >= maxn-1) ed = 1;
if(Find(z) == z) f[z] = k;
if(Find(x) == x) f[x] = k;
y = z;
x = pre[y];
}
}
void check(int x)//尽量帮助x寻找增广路
{
for(int i = 1; i <= n; i++) f[i] = i, id[i] = -1;
st = 1;
ed = 2;
que[st] = x;
id[x] = 0;
while(st != ed)
{
int u = que[st];
for(int i = head[u]; i != -1; i = edge[i].next)
{
int to = edge[i].v;
if(match[to] == 0 && to != x)
//当然match[X]=0,但X(这次来寻找配偶的点)并不是一个可行的东西,所以不能算可行解
{
pre[to] = u;
int last, t, now = to;
while(now != 0)
{
t = pre[now];
last = match[t];
match[t] = now;
match[now] = t;
now = last;
}
return;
}
if(id[to] == -1)//找到一个没有访问过的点————进行扩展
{
id[to] = 1;
pre[to] = u;//先假设他与x相连
id[match[to]] = 0;
que[ed++] = match[to];
// if(ed >= maxn-1) ed = 1;
}
else if(id[to] == 0 && Find(u) != Find(to))//出现一个以前未处理过的奇环
{
int g = lca(u, to);
change(u, to, g);
change(to, u, g);
}
}
st++;
// if(st >= maxn-1) st = 1;
}
}
int main()
{
memset(book, 0, sizeof(book));
memset(match, 0, sizeof(match));
memset(head, -1, sizeof(head));
edgecnt = 0;
tim = 0;
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);
}
for(int i = 1; i <= n; i++)
if(match[i] == 0)
check(i);
int ans = 0;
for(int i = 1; i <= n; i++)
if(match[i] != 0) ans++;
printf("%d\n", ans/2);
for(int i = 1; i <= n; i++)
printf("%d ", match[i]);
return 0;
}