Graph
Description
给定一张
n
个点
每次选择两条有公共点的两边匹配,被匹配过的边不能被再次匹配(即每条边最多匹配一次),输出最多的匹配对数和一种可行的匹配方案。
Data Constraint
n
<=
Solution
这题乍一看很难,其实想到了就很简单。(
To
be
or
not
to
be
)
对于每个联通块,先做一遍
dfs
,跑出一棵
dfs
树。
接下来,从深度大的节点做起,从下往上做。
对于每个点:
1’:若当前 与该点相连的边的数量为偶数,便让这些边两两匹配即可。
2’:若当前 与该点相连的边的数量为奇数,除了连向父亲的边以外,剩下的偶数条边两两匹配即可。
每一次匹配完的边要及时删除。
对于上述的匹配方案,
significantly
,若整个联通块内的总边数为偶数,则会在根节点恰好匹配完;若整个联通块内的总边数为偶数,则会在根节点恰好剩下一条边。
故上述匹配方案是最优策略。
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,j,l) for(int i=j;i<=l;i++)
#define fd(i,j,l) for(int i=j;i>=l;i--)
using namespace std;
typedef long long ll;
const ll N=1e5+1e3,M=4*N;
int used[M],lb[M],fa[N],la[N],ne[M],d[M],a1[M],a2[M],a3[M];
int n,m,k,l,o,cnt,x,y;
void read(int &o)
{
o=0; char ch=' ';
for(;ch<'0'||ch>'9';)ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar())o=o*10+ch-48;
}
void llb (int a,int b)
{
ne[++o]=la[a]; la[a]=o; lb[o]=b;
}
void addans(int a,int b,int c)
{
a1[++cnt]=a; a2[cnt]=b; a3[cnt]=c;
}
void dg(int o,int u)
{
int y=la[o],kt=0,ls=0;
while(y){
if(!fa[lb[y]])fa[lb[y]]=o,dg(lb[y],y);
if(!used[y]&&lb[y]!=fa[o])
{
if(!kt)kt=1,ls=y;
else {
kt=0;
addans(lb[ls],o,lb[y]);
used[ls]=used[d[ls]]=1;
used[y]=used[d[y]]=1;
}
}
y=ne[y];
}
if(u&&kt==1){
addans(lb[ls],o,lb[d[u]]);
used[ls]=used[d[ls]]=1;
used[u]=used[d[u]]=1;
}
}
int main()
{
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
cin>>n>>m;
fo(i,1,m){
read(x);read(y);
llb(x,y);llb(y,x);
d[o]=o-1; d[o-1]=o;
}
cnt=0;
fo(i,1,n)
if(!fa[i])
fa[i]=-1,dg(i,0);
printf("%d\n",cnt);
fo(i,1,cnt)printf("%d %d %d\n",a1[i],a2[i],a3[i]);
}