欧拉路径
参考资料: 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
作者:pjykk
出处:https://www.cnblogs.com/pjykk/p/16555636.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现