Codeforces 1385E - Directing Edges(拓扑排序判环)

E. Directing Edges

time limit per test3 seconds
memory limit per test256 megabytes
input standard input
output standard output
You are given a graph consisting of n vertices and m edges. It is not guaranteed that the given graph is connected. Some edges are already directed and you can’t change their direction. Other edges are undirected and you have to choose some direction for all these edges.

You have to direct undirected edges in such a way that the resulting graph is directed and acyclic (i.e. the graph with all edges directed and having no directed cycles). Note that you have to direct all undirected edges.

You have to answer t independent test cases.

Input

The first line of the input contains one integer t (1≤t≤2⋅104) — the number of test cases. Then t test cases follow.

The first line of the test case contains two integers n and m (2≤n≤2⋅105, 1≤m≤min(2⋅105,n(n−1)2)) — the number of vertices and the number of edges in the graph, respectively.

The next m lines describe edges of the graph. The i-th edge is described with three integers ti, xi and yi (ti∈[0;1], 1≤xi,yi≤n) — the type of the edge (ti=0 if the edge is undirected and ti=1 if the edge is directed) and vertices this edge connects (the undirected edge connects vertices xi and yi and directed edge is going from the vertex xi to the vertex yi). It is guaranteed that the graph do not contain self-loops (i.e. edges from the vertex to itself) and multiple edges (i.e. for each pair (xi,yi) there are no other pairs (xi,yi) or (yi,xi)).

It is guaranteed that both sum n and sum m do not exceed 2⋅105 (∑n≤2⋅105; ∑m≤2⋅105).

Output

For each test case print the answer — “NO” if it is impossible to direct undirected edges in such a way that the resulting graph is directed and acyclic, otherwise print “YES” on the first line and m lines describing edges of the resulted directed acyclic graph (in any order). Note that you cannot change the direction of the already directed edges. If there are several answers, you can print any.

Example
input
4
3 1
0 1 3
5 5
0 2 1
1 1 5
1 5 4
0 5 2
1 3 5
4 5
1 1 2
0 4 3
1 3 1
0 2 3
1 2 4
4 5
1 4 1
1 1 3
0 1 2
1 2 4
1 3 2
output
YES
3 1
YES
2 1
1 5
5 4
2 5
3 5
YES
1 2
3 4
3 1
3 2
2 4
NO
Note
Explanation of the second test case of the example:
在这里插入图片描述

Explanation of the third test case of the example:
在这里插入图片描述

题目大意:

给出 t 组测试用例,对于每组样例,输入n表示这个图共有n个节点,输入m表示有m条边,接下来是m行输入,先输入0 或 1 表示有向无向,然后输入两个节点a,b,如果是1 则说明a -> b有一条有向边,判断能否通过对无向边的定向来使得图中没有自环,如果可以则输出“YES”,并以任意顺序输出m条有向边,如果发现自环则输出“NO”。

解题思路:

这道题的思路是如果遇到有向边存入有向边vector v,然后无论是否有方向都存入存边的向量edge,首先对有向边向量v进行dfs遍历求拓扑序,注意此时求的拓扑序是倒着的,需要reverse
然后给每个顶点打上拓扑序的编号,之后判环。判环时,如果当前顶点的拓扑序大于当前顶点发出的边的拓扑序,则说明有环,输出NO并continue。
如果没有环,则根据拓扑序依次输出边即可。输出是,如果y节点的拓扑序 > y节点的拓扑序,就让 y 在前,如果x的拓扑序小于y的拓扑序就让x在前,遍历完整张图即可。

AC代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
using namespace std;
vector<vector<int > > v;
vector<int > vis;
vector<int > tp;
struct node {int x, y; };
void dfs(int p)
{
    vis[p] = 1;
    for (auto i : v[p])
    {
        if (!vis[i])
          dfs(i);
    }
    tp.push_back(p);//注意只能写在这里
}
int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        int n, e;
        cin >> n >> e;
        vector<node > edge;
        v = vector<vector<int> >(n);
        while (e--)
        {
            int f, a, b;
            cin >> f >> a >> b;
            a--, b--;//这里我从0开始遍历,所以都 -- ,输出的时候再加回来就行
            if (f == 1)
                v[a].push_back(b);
            edge.push_back({a, b});
        }
        tp.clear();
        vis = vector<int >(n);
        for (int i = 0; i < n; i ++)
          if (!vis[i])
            dfs(i);
        reverse(tp.begin(), tp.end());//一开始排好的是逆序,需要反转
        vector<int > pos(n);
        for (int i = 0; i < n; i ++)
            pos[tp[i]] = i;
        bool ok = false;
        for (int i = 0; i < n; i ++)
        {
            for (auto j : v[i])
              if (pos[i] > pos[j])
              {
                  ok = true;
                  break;
              }
        }
        if (ok)
        {
            cout << "NO" << endl;
            continue;
        }
        cout << "YES" << endl;
        for (auto [x, y] : edge)
        {
            if (pos[x] > pos[y])//根据拓扑关系选择先后顺序
              cout << y + 1 << " " << x + 1 << endl;
            else
              cout << x + 1 << " " << y + 1 << endl;
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/aezakmias/article/details/107434279
今日推荐