欧拉回路:终点就是起点
一、无向图
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;
}