欧拉回路 DFS T3 #10105. 「一本通 3.7 例 1」欧拉回路 - 题目 - LibreOJ (loj.ac) 有一天一位灵魂画师画了一张图,现在要你找出欧拉回路,即在图中找一个环使得每条边都在环上出现恰好一次。 一共两个子任务:

  1. 这张图是无向图。( 50 分)
  2. 这张图是有向图。( 50 分)

输入格式

第一行一个整数 ,表示子任务编号。 ,如果 则表示处理无向图的情况,如果 则表示处理有向图的情况。

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

接下来 行中,第 行两个整数 ,表示第 条边(从 开始编号)。保证

  1. 如果 则表示 有一条无向边。
  2. 如果 则表示 有一条有向边。

图中可能有重边也可能有自环。

输出格式

如果不可以一笔画,输出一行 NO。 否则,输出一行 YES,接下来一行输出一组方案。

  1. 如果 ,输出 个整数 。令 ,那么 表示经过的第 条边的编号。如果 为正数表示从 走到 ,否则表示从 走到
  2. 如果 ,输出 个整数 。其中 表示经过的第 条边的编号。

样例 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;
}