【洛谷P3916】图的遍历

今天开始着重学习图论

还是比较基础的阶段 直接看题目

P3916 图的遍历

题目描述

给出 \(N\) 个点,\(M\) 条边的有向图,对于每个点 \(v\),求 \(A(v)\) 表示从点 \(v\) 出发,能到达的编号最大的点。

输入格式

\(1\)\(2\) 个整数 \(N,M\),表示点数和边数。

接下来 \(M\) 行,每行 \(2\) 个整数 \(U_i,V_i\),表示边 \((U_i,V_i)\)。点用 \(1,2,\dots,N\) 编号。

输出格式

一行 \(N\) 个整数 \(A(1),A(2),\dots,A(N)\)

输入输出样例 #1

输入 #1

4 3
1 2
2 4
4 3

输出 #1

4 4 3 4

说明/提示

  • 对于 \(60\%\) 的数据,\(1 \leq N,M \leq 10^3\)
  • 对于 \(100\%\) 的数据,\(1 \leq N,M \leq 10^5\)

解法&&个人感想

看到什么一个点到多个点的题目 我们要想 这种题目是可以转化的
转化成多个点到一个点 然后反向建边
这个时候可能有人会说,诶这不是时间复杂度一样吗?
其实在这题是不一样的
我们容易看出 反向建边后从序号较大的节点DFS 如果一个节点被访问过 那么访问的起始点必定为其最大值
这个很好理解 所以在本题反向建边是避免TLE的 好像有数据卡正向建边
下面看代码:

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
int n,m,x,y,tot;
int head[100005],ver[100005],nex[100005];
int ans[100005];
void add(int x,int y){
    ver[++tot]=y;
    nex[tot]=head[x];
    head[x]=tot;
}
void dfs(int now,int fa){
    if(ans[now]) return ;
    ans[now]=fa;
    for(int i=head[now];i;i=nex[i]){
        int y=ver[i];
        dfs(y,fa);
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        add(y,x);
    }
    for(int i=n;i>=1;i--){
        if(!ans[i]) dfs(i,i);
    }
    for(int i=1;i<=n;i++){
        cout<<ans[i]<<' ';
    }
    system("pause");
    return 0;
}
posted @ 2025-02-17 09:21  elainafan  阅读(69)  评论(0)    收藏  举报