Loading

欧拉路径

参考资料: OI-wiki

0. 一些概念

首先让我们明确几个概念.

我们定义, 通过图中所有边恰好一次的通路称为欧拉通路, 若该路为回路则称为欧拉回路.
若一个图存在欧拉回路, 则称该图为欧拉图. 若不存在欧拉回路但存在欧拉通路, 该图成为半欧拉图.

无向图 (半) 欧拉图判定:

  • 一个连通图是无向欧拉图等价于该图中点的度数均为偶数.
  • 一个连通图是无向半欧拉图等价于该图中只有两个点的度数为奇数 (起点和终点).

上面两个判定是众所周知的.

对于有向图, 我们也可以进行判定:

  • 一个图是有向欧拉图等价于该图为一个 SCC 且每个点的入度和出度相同.
  • 一个图是有向半欧拉图等价于:
    • 若将边都视为无向边则该图连通;
    • 只存在一个点, 它的出度等于入度加一, 该点为起点;
    • 只存在一个点, 它的入度等于出度加一, 该点为终点;
    • 除起点和终点外的所有点入度和出度相同.

也很容易理解.

1. 求欧拉路径

luoguP7771 【模板】欧拉路径

题意: 给定一个有向图, 输出其字典序最小的欧拉路径 (不存在输出 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. 一些例题

CF508D Tanya and Password

luoguP3520 [POI2011] SMI-Garbage

luoguP3443 [POI2006]LIS-The Postman

CF527E Data Center Drama

posted @ 2022-11-18 19:03  pjykk  阅读(60)  评论(0编辑  收藏  举报