Codeforces Round #503 (by SIS, Div. 2) B. Badge(dfs、拓扑排序、并查集)

题目:

  • 题意:样例给出每点与之所指向的顶点,形成一张有向图,询问n次,第i次询问则从第i个点出发,计算出哪个顶点第一次被走第二遍。
  • 思路:裸dfs、拓扑排序、记忆化搜索
  • 解法一:裸的dfs,根据图给出的有向路线去搜一次,并把每次到达的点做一个标记则第一次访问到曾经标记过的点,则该点为第一个走两次的点。该算法时间复杂度为:O(N^2)
  • 代码:
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1005;
int a[N], vis[N];
int n;

void dfs(int pos)
{
    if(vis[pos]) cout << pos << " ";
    else
    {
        vis[pos] = 1;
        dfs(a[pos]);
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin >> n;
    for(int i = 1; i <= n; i++)
        cin >> a[i];
    for(int i = 1; i <= n; i++)
    {
        memset(vis, 0, sizeof vis);
        dfs(i);
    }
    return 0;
}

  • 解法二:因为可以得知,当一个点在图中的环上时,则答案为自身,否则答案为自身相邻最近在环上的顶点。那么一开始默认每个顶点都在环上(自身成环),接着通过拓扑排序来去除不是在环上的点,将其置为-1,之后用并查集路径压缩的方法来记忆化搜索不在环上的所有点距离最近在环上的点。该算法时间复杂度可达到:O(N)

  • 图解:

  • 代码:

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int N = 1005;
int a[N], ans[N], d[N];
int n;
queue<int> q;

int find_ans(int x)
{
    if(x == ans[x]) return x; //若该点在一个环上则答案为自身
    else return ans[x] = find_ans(a[x]); //否则答案为相邻最近在环上的点(并查集路径压缩)
}
void topoSort()
{
    for(int i = 1; i <= n; i++)
        if(!d[i]) q.push(i);
    while(q.size())
    {
        int node = q.front();
        q.pop();
        int nextNode = a[node];
        ans[node] = -1; //入度为0的顶点肯定不在环上(置为-1),所以最终答案不会是自身,而是离自身最近且在环上的点
        d[nextNode] --; //相邻顶点入度减一
        if(!d[nextNode]) q.push(nextNode);
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    memset(d, 0, sizeof d);
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        ans[i] = i; //假设一开始每个点自身看作一个环
        d[a[i]] ++; //顶点a[i]入度+1
    }
    topoSort();
    for(int i = 1; i <= n; i++)
        cout << find_ans(i) << " ";
    return 0;
}
posted @ 2021-01-21 16:45  ~K2MnO4  阅读(94)  评论(0)    收藏  举报