「CodeNote」 反向拓扑

Posted by Dawn-K's Blog on November 7, 2019

反向拓扑

介绍

反向拓扑是一个很常见的算法.解决的问题也很模板,是这样的一个问题:

有N个人,M个优先级a,b表示a优先于b。要求 :编号最小的节点要尽量排在前面;在满足上一个条件的基础上,编号第二小的节点要尽量排在前面;在满足前两个条件的基础上,编号第三小的节点要尽量排在前面……依此类推。(注意,这和字典序是两回事,不能够混淆

思路

这个思路初见还是真不好想.需要逆向思维.

6->1->3

5->2->4

以这两个为例,显然需要先令第一条链优先弹出.但是我们发现,在朴素的拓扑过程中,很难得到正确的结果,因为我们面对一条链的开始的时候,很难第一时间判断到底该选哪一个.

逆向思维.我们发现,各个链根据最后一个的元素的选择是确定的,因为前面的已经被弹走了,没有后顾之忧.也就是说,对于”让大的元素尽可能的向后放“是一个简单的贪心问题.我们只需要关注每个链的最后一个,然后选择他们之中最大的就可以.

事实上这样的方法正是解决之前问题的算法.

为了方便计算,我们反向建图,然后采取和字典序最小的算法类似的方法.求最大的字典序,得到的答案翻转过来,就是题目所要求的答案.

模板

HDU4857

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// #include <bits/stdc++.h>
#include <cstdio>
#include <iostream>
#include <queue>
#include <vector>

using namespace std;
#define maxn 30000+100
#define maxm 100000 + 10
#define ll long long
#define endl '\n'
#define pii pair<int, int>
#define fi first
#define se second
#define pb push_back
struct {
    int v, next;
} edge[2 * maxm];
int cnt;
int head[maxn];
int out[maxn];
int n, m;
void init(int rng) {
    for (int i = 0; i <= rng; ++i) {
        head[i] = -1;
    }
    cnt = 0;
}
void add(int u, int v) {
    edge[cnt].v = v;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}
void topo2() {  
    priority_queue<int, vector<int>, less<int>> q;
    for (int i = 1; i <= n; ++i) {
        if (!out[i]) {
            q.push(i);
        }
    }
    vector<int> ans;
    while (!q.empty()) {
        int cur = q.top();
        ans.pb(cur);
        q.pop();
        for (int i = head[cur]; i + 1; i = edge[i].next) {
            int v = edge[i].v;
            if (!out[v])
                continue;
            --out[v];
            if (!out[v]) {
                q.push(v);
            }
        }
    }
    if (ans.size() < n) {
        printf("-1\n");
    } else {
        for (int i = n - 1; i >= 0; --i) {
            printf("%d%c", ans[i], i ? ' ' : '\n');
        }
    }
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &m);
        init(n);
        for (int i = 0; i <= n; ++i) {
            out[i] = 0;
        }
        for (int i = 0; i < m; ++i) {
            int u, v;
            scanf("%d%d", &u, &v);
            add(v, u);
            ++out[u];
        }
        topo2();
    }
    return 0;
}