欧拉回路 DFS T3 #10105. 「一本通 3.7 例 1」欧拉回路 - 题目 - LibreOJ (loj.ac) 有一天一位灵魂画师画了一张图,现在要你找出欧拉回路,即在图中找一个环使得每条边都在环上出现恰好一次。 一共两个子任务:
- 这张图是无向图。( 50 分)
- 这张图是有向图。( 50 分)
输入格式
第一行一个整数 ,表示子任务编号。 ,如果 则表示处理无向图的情况,如果 则表示处理有向图的情况。
第二行两个整数 ,表示图的结点数和边数。
接下来 行中,第 行两个整数 ,表示第 条边(从 开始编号)。保证 。
- 如果 则表示 到 有一条无向边。
- 如果 则表示 到 有一条有向边。
图中可能有重边也可能有自环。
输出格式
如果不可以一笔画,输出一行 NO
。
否则,输出一行 YES
,接下来一行输出一组方案。
- 如果 ,输出 个整数 。令 ,那么 表示经过的第 条边的编号。如果 为正数表示从 走到 ,否则表示从 走到 。
- 如果 ,输出 个整数 。其中 表示经过的第 条边的编号。
样例 1
输入
1
3 3
1 2
2 3
1 3
输出
YES
1 2 -3
样例 2
输入
2
5 6
2 3
2 5
3 4
1 2
4 2
5 1
输出
YES
4 1 3 5 2 6
数据范围与提示
思路
求欧拉回路的话, 需要先判断是否有解: 1)有向图时, 只有所有点的入度等于出度才算有解 2)无向图时, 所有点的度数都是偶数才算有解
加边时统计度数, 然后再DFS搜索路径, 如果路径长度小于m, 即没有搜到所有边, 那么也算无解。
搜的时候若朴素暴力搜:
dfs()
for (i=h[u] ; i = ne[i])
if(used[i])
h[u] = ne[i]
continue
h[u] = ne[i]
if(type == 1) used[i ^ 1] = true;
dfs(e[i])
因为DFS会先搜到最低端然后再回溯, 而标记边时只能标记已经搜过的, 那么回溯之后有可能拐到之前的边, 极端情况下还是 的复杂度。
这里可以在枚举时直接让 , 边缩边搜, 就是 复杂度了。 如果碰到之前搜过的边 则直接 跳过即可。若没搜过则加入到结果序列中, 注意这里是添加编号, 且若为无向边则只添加一条边。 有向边的编号就是 , 而无向图的编号则是 , 题目中还要求如果是反向边需要输出负数, 因为正反两条边添加是一起的, 第一条, 第二条 故正向边都会是奇数, 而反向边都是偶数。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
const int N = 1e5 + 10, M = 4e5 + 10;
int h[N], ne[M], e[M], idx;
int n, m;
int type;
int din[N], dout[N];
bool used[M];
int cnt, res[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;
int t;
if (type == 1)
{
used[i ^ 1] = true;
t = i / 2 + 1;
if (i & 1)
t = -t;
}
else
t = i + 1;
int j = e[i];
i = ne[i];
dfs(j);
res[++cnt] = t;
}
}
int main()
{
memset(h, -1, sizeof h);
cin >> type;
cin >> n >> m;
for (int i = 0; i < m; i++)
{
int a, b;
cin >> a >> b;
add(a, b);
if (type == 1)
add(b, a);
din[b]++;
dout[a]++;
}
if (type == 1)
{
for (int i = 1; i <= n; i++)
if ((din[i] + dout[i]) & 1)
{
cout << "NO\n";
return 0;
}
}
else if (type == 2)
{
for (int i = 1; i <= n; i++)
if (din[i] != dout[i])
{
cout << "NO\n";
return 0;
}
}
for (int i = 1; i <= n; i++)
if (h[i] != -1)
{
dfs(i);
break;
}
if (cnt < m)
{
cout << "NO\n";
return 0;
}
cout << "YES\n";
for (int i = cnt; i; i--)
cout << res[i] << " ";
return 0;
}