欧拉回路和欧拉路径

 欧拉回路:终点就是起点
一、无向图
   1 存在欧拉路径的充要条件 : 度数为奇数的点只能有0或2个
   2 存在欧拉回路的充要条件 : 度数为奇数的点只能有0个
二、有向图
   1 存在欧拉路径的充要条件 : 要么所有点的出度均==入度;
                              要么除了两个点之外,其余所有点的出度==入度 剩余的两个点:一个满足出度-入度==1(起点) 一个满足入度-出度==1(终点)
   2 存在欧拉回路的充要条件 : 所有点的出度均等于入度

  _O_ 环可以合并进边  或者环可以和环合并 OO

     o
    _|_
   | |_|_
   |   |_|
   o
   对于除了起点与终点外中间的点  
   由于它们的度数是偶数 则只要它从某一个过环的出度出发 则必然会走完这个环回到这个点

   合并环的方法:有环先走环
dfs(u)
{
 for 从n出发的所有边
    dfs() 扩展
 把u加入序列  seq[]←u
}
最终 seq[]中存下的就是欧拉路径的倒序

有向图:
      每用一条边 删掉
无向图:
      每用一条边标记对应的反向边(XOR技巧)
      a→b
      b→a

 

 

 

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'

const int N = 1e5 + 1000, M = 4e5 + 1000;
int h[N], e[M], ne[M], idx;
int din[N], dout[N], ans[M], cnt;
int type, n, m;
bool used[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; )
    {
       if(used[i]) // 如果这条边用过了
       {
         i = ne[i]; // 删除这条边
         continue;
       }

       used[i] = true;
       if(type == 1) used[i ^ 1] = true; //如果是无向图,那么这条边的反向边也要标记使用过了

       int t; // 得到当前边的编号
       if(type == 1)
       {
         t = i / 2 + 1;
         if(i & 1) t = -t;  //(0,1) (2,3) (4,5)
       }
       else t = i + 1;

       int j = e[i];
       i = ne[i];
       dfs(j);
       ans[cnt++] = t;
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> type >> n >> m;
    memset(h, -1, sizeof h);
    for(int i = 1; i <= m; i++)
    {
      int a, b;
      cin >> a >> b;
      add(a, b);
      din[b]++, dout[a]++;
      if(type == 1) add(b, a); // 无向图
    }

    if(type == 1) {
      for(int i = 1; i <= n; i ++)
       if(din[i] + dout[i] & 1)
       {
         cout << "NO" << endl;
         return 0;
       }
    }
    else {
      for(int i = 1; i <= n; i++)
        if(din[i] != dout[i])
        {
          cout << "NO" << endl;
          return 0;
        }
    }

    for(int i = 1; i <= n; i++)
      if(~h[i]) // 不是孤立点(有边连接)
      {
         dfs(i);
         break;
      }

    if(cnt < m){  // 有边不连通
      cout << "NO" << endl;  
      return 0;
    }

    cout << "YES" << endl;
    for(int i = cnt - 1; i >= 0; i--)
      cout << ans[i] << " \n"[i == 0];

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值