欧拉路径
参考资料: OI-wiki
0. 一些概念
首先让我们明确几个概念.
我们定义, 通过图中所有边恰好一次的通路称为欧拉通路, 若该路为回路则称为欧拉回路.
若一个图存在欧拉回路, 则称该图为欧拉图. 若不存在欧拉回路但存在欧拉通路, 该图成为半欧拉图.
无向图 (半) 欧拉图判定:
- 一个连通图是无向欧拉图等价于该图中点的度数均为偶数.
- 一个连通图是无向半欧拉图等价于该图中只有两个点的度数为奇数 (起点和终点).
上面两个判定是众所周知的.
对于有向图, 我们也可以进行判定:
- 一个图是有向欧拉图等价于该图为一个 SCC 且每个点的入度和出度相同.
- 一个图是有向半欧拉图等价于:
- 若将边都视为无向边则该图连通;
- 只存在一个点, 它的出度等于入度加一, 该点为起点;
- 只存在一个点, 它的入度等于出度加一, 该点为终点;
- 除起点和终点外的所有点入度和出度相同.
也很容易理解.
1. 求欧拉路径
题意: 给定一个有向图, 输出其字典序最小的欧拉路径 (不存在输出 No
).
首先根据上面的判定定理我们容易判定该图是否为(半)欧拉图并找出欧拉路径的起点和终点.
然后直接 dfs 就完事了. 每次我们选择第一条还没有走过的路径, 返回的时候把点压入栈即可.
需要注意的是我们需要注意一点细节, 需要记录现在已经选到第几条边了, 不要重复遍历已经走过的边.
要保证字典序最小只需要对邻接表排一下序就行了.
code:
const int maxn=100010;
int n,m,s=1,t,scnt,tcnt,indeg[maxn],outdeg[maxn],cur[maxn];
vector<int> G[maxn];
stack<int> path;
void dfs(int u)
{
for(int i=cur[u];i<G[u].size();i=cur[u])
{
cur[u]=i+1;
dfs(G[u][i]);
}
path.push(u);
}
bool check()
{
for(int i=1;i<=n;i++)
{
if(indeg[i]==outdeg[i])continue;
if(indeg[i]==outdeg[i]+1)
{
tcnt++;
t=i;
if(tcnt>1)return 0;
continue;
}
if(indeg[i]+1==outdeg[i])
{
scnt++;
s=i;
if(scnt>1)return 0;
continue;
}
return 0;
}
return 1;
}
int main()
{
n=read();m=read();
for(int i=1;i<=m;i++)
{
int u=read(),v=read();
indeg[v]++;outdeg[u]++;
G[u].push_back(v);
}
if(!check())
{
printf("No");
return 0;
}
for(int i=1;i<=n;i++)sort(G[i].begin(),G[i].end());
dfs(s);
while(!path.empty())
{
printf("%d ",path.top());
path.pop();
}
return 0;
}
2. 一些例题
luoguP3520 [POI2011] SMI-Garbage