hdu6311 Cover(欧拉回路)
题意
给定一张无向图(不保证联通),求把所有的边分成若干个集合,使得每个集合都是一个欧拉路,求划分的集合个数最少,并给出构造方案。
回顾
在图上用一种走法经过所有的边恰好一次的路径叫做欧拉路径。
如果这条路径的起点和终点重合,那么就是欧拉回路。
为了方便,下文的“欧拉路径”均指满足上述性质且不是回路的路径。
判断
-
无向图
- 欧拉回路:图中每个点度数为偶。
- 欧拉路径:恰好有两个点度数为奇。
-
有向图
- 欧拉回路:图中每个点入度 \(=\) 出度
- 欧拉路径:最多两个点入度 \(\ne\) 出度,且其中一个点(起点)出度 \(=\) 入度 \(+1\),另一个(终点)入度 \(=\) 出度 \(+1\)。
必要性显然,下面给出较简单的构造方法:Hierholzer 算法。
构造
-
判断是否存在欧拉路,若只存在路径则找到起点,若存在回路则可以以任意点作为起点开始。
-
开始递归函数 Hierholzer(u):
- 寻找与 u 相连的边 (u,v):
- 删除 (u,v)
- 删除 (v,u) (无向图)
- 递归 Hierholzer(v)
- 将 u 加入序列尾
- 寻找与 u 相连的边 (u,v):
-
得到欧拉路序列
此时序列中为找到的欧拉路的点的倒序,求边的顺序稍微修改一下即可。
感性理解一下,Hierholzer 算法的流程实际上是一开始找到一条简单路径(回路)连接起点到终点作为骨架,然后剩下的一些没被走到过的边实际上是一个个小的欧拉回路连接在骨架上,递归处理这些小的欧拉回路,最后按顺序拼接到骨架上,即可找到整个图的欧拉路。
思路
首先,如果一张联通图没有奇度点,显然可以直接分配。
一个经典结论:联通无向图中奇度点一定有偶数个。这启示我们最优分配方案一定是奇度点间两两搭配,分别作为一个欧拉路经的起点和终点。
如何构造?
一个很自然的想法就是新建一个超级源点,将所有奇度点向超级源连无向边,然后从没有奇度点的联通块任意选一个点向超级源连两条无向边,这样就能使整个图联通且所有点都是偶度点。
直接对新图跑欧拉路,得到的边序列以新加的边为间隔分成若干段,每段即为一条欧拉路。
代码
点击查看代码
#include<bits/stdc++.h>
#define RG register
#define R RG int
#define I inline
using namespace std;
const int N=1e5+3,M=2e5+3;
struct edge{int to,nxt;}e[M<<1];
bool nv[M],dg[N];
int lst[N],tt=1;
I void link(int u,int v)
{
e[++tt]=(edge){v,lst[u]};lst[u]=tt;
e[++tt]=(edge){u,lst[v]};lst[v]=tt;
dg[u]^=1;dg[v]^=1;
}
bool vs[N];
int tc;
void dfs(int u)
{
if(vs[u])return;
vs[u]=1;++tc;
if(dg[u])link(u,0);
for(R i=lst[u];i;i=e[i].nxt)
dfs(e[i].to);
}
int sk[M<<1],tk;
void find(int ei)
{
int u=e[ei].to;
for(R &i=lst[u];i;i=e[i].nxt)
{
if(nv[i>>1])continue;
nv[i>>1]=1;
find(i);
}
sk[++tk]=ei;
}
int main()
{
int n,m,ans=0;
scanf("%d%d",&n,&m);
for(R i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
link(x,y);
}
vs[0]=1;
for(R i=1;i<=n;i++)
{
if(vs[i])continue;
int nt=tt,nc=tc;
dfs(i);
if(tc==nc+1)continue;
if(tt==nt)link(i,0),link(i,0);
ans+=tt-nt>>2;
}
e[1].to=0;
find(1);
printf("%d\n",ans);
for(R i=tk-1,r=i;i;i--)
{
int ei=sk[i];
if(ei>>1>m)
{
if(i<r)
{
printf("%d",r-i);
for(R j=r;j>i;j--)
{
int ej=sk[j];
printf(" %d",ej&1?-(ej>>1):ej>>1);
}
puts("");
}
r=i-1;
}
}
return 0;
}
浙公网安备 33010602011771号