欧拉回路(输出路径) - 欧拉回路 - AcWing 1184

欧拉回路(输出路径) - 欧拉回路 - AcWing 1184

给定一张图,请你找出欧拉回路,即在图中找一个环使得每条边都在环上出现恰好一次。

输入格式

第一行包含一个整数 t,t∈{1,2},如果 t=1,表示所给图为无向图,如果 t=2,表示所给图为有向图。

第二行包含两个整数 n,m,表示图的结点数和边数。

接下来 m 行中,第 i 行两个整数 vi,ui,表示第 i 条边(从 1 开始编号)。

如果 t=1 则表示 vi 到 ui 有一条无向边。
如果 t=2 则表示 vi 到 ui 有一条有向边。
图中可能有重边也可能有自环。

点的编号从 1 到 n。

输出格式

如果无法一笔画出欧拉回路,则输出一行:NO。

否则,输出一行:YES,接下来一行输出 任意一组 合法方案即可。

如果 t=1,输出 m 个整数 p1,p2,…,pm。令 e=|pi|,那么 e 表示经过的第 i 条边的编号。如果 pi 为正数表示从 ve 走到 ue,否则表示从 ue 走到 ve。
如果 t=2,输出 m 个整数 p1,p2,…,pm。其中 pi 表示经过的第 i 条边的编号。

数据范围

1 ≤ n ≤ 1 0 5 , 0 ≤ m ≤ 2 × 1 0 5 1≤n≤10^5, 0≤m≤2×10^5 1n105,0m2×105

输入样例1:

1
3 3
1 2
2 3
1 3

输出样例1:

YES
1 2 -3

输入样例2:

2
5 6
2 3
2 5
3 4
1 2
4 2
5 1

输出样例2:

YES
4 1 3 5 2 6

分析:

欧拉路径/回路存在的充要条件:

1、对于无向图:

① 、 欧 拉 路 径 : 度 数 为 奇 数 的 点 只 能 有 0 或 2 个 。 ①、欧拉路径:度数为奇数的点只能有0或2个。 02

② 、 欧 拉 回 路 : 度 数 为 奇 数 的 点 只 能 有 0 个 。 ②、欧拉回路:度数为奇数的点只能有0个。 0

2、对于有向图:

① 、 欧 拉 路 径 : 所 有 点 的 出 度 等 于 入 度 , 或 者 , 一 个 点 的 出 度 比 入 度 多 1 ( 起 点 ) , 一 个 点 的 入 度 比 出 度 多 1 ( 终 点 ) , 其 他 点 的 入 度 与 出 度 相 等 。 ①、欧拉路径:所有点的出度等于入度,或者,\\\qquad一个点的出度比入度多1(起点),一个点的入度比出度多1(终点),其他点的入度与出度相等。 1()1()

② 、 欧 拉 回 路 : 所 有 点 的 出 度 等 于 入 度 。 ②、欧拉回路:所有点的出度等于入度。

遍历欧拉回路:

从 出 度 不 为 0 的 点 开 始 进 入 搜 索 。 从出度不为0的点开始进入搜索。 0

注 意 , 先 深 搜 到 底 , 再 将 点 加 入 队 列 。 注意,先深搜到底,再将点加入队列。

最 后 倒 序 输 出 队 列 , 得 到 的 欧 拉 路 径 。 最后倒序输出队列,得到的欧拉路径。

优化:

由 于 每 条 边 仅 需 遍 历 一 次 , 为 了 避 免 很 多 自 环 出 现 在 同 一 个 点 上 的 情 况 , 由于每条边仅需遍历一次,为了避免很多自环出现在同一个点上的情况,

我 们 每 遍 历 一 条 边 就 跳 过 一 条 边 ( 删 除 ) , 体 现 在 将 邻 接 表 表 头 后 移 , 我们每遍历一条边就跳过一条边(删除),体现在将邻接表表头后移, ()

另 外 , 无 向 图 建 图 时 , 每 组 边 的 编 号 依 次 为 ( 0 , 1 ) 、 ( 2 , 3 ) 、 . . . ( 2 × m − 2 , 2 × m − 1 ) , 另外,无向图建图时,每组边的编号依次为(0,1)、(2,3)、...(2×m-2,2×m-1), (0,1)(2,3)...(2×m2,2×m1)

为 了 删 除 某 条 边 的 反 向 边 , 可 以 对 该 边 的 编 号 异 或 1 , 如 0 ⨁ 1 = 1 , 2 ⨁ 1 = 3 。 为了删除某条边的反向边,可以对该边的编号异或1,如0\bigoplus1=1,2\bigoplus1=3。 101=121=3

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=100010, M=400010;

int t,n,m;
int e[M],ne[M],h[N],idx;
int ans[M/2],cnt;
int din[N],dout[N];
bool st[M];

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void dfs(int u)
{
    for(int &i=h[u];~i;)  //因为要求欧拉回路,我们从一个点进入就能够遍历到所有边,
    {                    //每次都从h[u]所指向的边继续向下搜索,每次都删除一条边
        if(st[i])
        {
            i=ne[i];    //跳过该边(删除)
            continue; 
        }
        
        st[i]=true;
        if(t==1) st[i^1]=true;  //无向边
        
        int id;
        if(t==1) 
        {
            id=i/2+1;
            if(i & 1) id=-id;
        }
        else id=i+1;
        
        int j=e[i];
        i=ne[i];    //先跳过该边(删除),再进入搜索
        dfs(j);
        
        ans[++cnt]=id;  //先搜索再记录答案,因为要一笔画
    }
}

int main()
{
    scanf("%d%d%d",&t,&n,&m);
    
    memset(h,-1,sizeof h);
    int a,b;
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&a,&b);
        add(a,b);
        if(t==1) add(b,a);
        din[b]++,dout[a]++;
    }
    
    if(t==1)
    {
        for(int i=1;i<=n;i++)
            if(din[i]+dout[i] & 1)
            {
                puts("NO");
                return 0;
            }
    }
    else
    {
        for(int i=1;i<=n;i++)
            if(din[i]!=dout[i])
            {
                puts("NO");
                return 0;
            }
    }
    
    for(int i=1;i<=n;i++)   //从一个非孤立点进入搜索
        if(h[i]!=-1)
        {
            dfs(i);
            break;
        }
    
    if(cnt<m)
    {
        puts("NO");
        return 0;
    }

    puts("YES");
    for(int i=cnt;i;i--) printf("%d ",ans[i]);  //倒序输出
    puts("");
    
    return 0;
}
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值