【洛谷】P3916 图的遍历

题目地址:

https://www.luogu.com.cn/problem/P3916

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

输入格式:
1 1 1行, 2 2 2个整数 N , M N,M N,M
接下来 M M M行,每行 2 2 2个整数 U i , V i U_i,V_i Ui,Vi,表示边 ( U i , V i ) (U_i,V_i) (Ui,Vi)。点用 1 , 2 , ⋯   , N 1, 2,\cdots,N 1,2,,N编号。

输出格式:
N N N个整数 A ( 1 ) , A ( 2 ) , ⋯   , A ( N ) A(1),A(2),\cdots,A(N) A(1),A(2),,A(N)

数据范围:
对于 60 % 60\% 60%的数据, 1 ≤ N , M ≤ 1 0 3 1 \le N, M \le 10^3 1N,M103
对于 100 % 100\% 100%的数据, 1 ≤ N , M ≤ 1 0 5 1 \le N, M \le 10^5 1N,M105

如果是无环图的话,则由于其能拓扑排序,可以按照拓扑排序的逆序向前递推,设 f [ u ] f[u] f[u] u u u能到达的编号最大的点的编号,则有: f [ u ] = max ⁡ u → v { f [ v ] , u } f[u]=\max_{u\to v}\{f[v],u\} f[u]=uvmax{f[v],u}但这种方法在这道题中是行不通的。如果有环,会有循环依赖。可以用Tarjan算法先求强联通分量,然后缩点,接着可以利用拓扑序逆序递推。但这道题还有更简单的做法,只需要建立反向图,然后依次从点 n , n − 1 , . . . , 1 n,n-1,...,1 n,n1,...,1开始DFS,原图中每个点能到达的编号最大的点就成为了当前图里每个点能到达哪里,这样就很好做了。代码如下:

#include <iostream>
#include <cstring>
using namespace std;

const int N = 1e5 + 10;
int n, m;
int h[N], e[N], ne[N], idx;
int res[N];

void add(int a, int b) {
  e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void dfs(int u, int c) {
  if (res[u]) return;

  res[u] = c;
  for (int i = h[u]; ~i; i = ne[i]) {
    int v = e[i];
    dfs(v, max(v, c));
  }
}

int main() {
  memset(h, -1, sizeof h);

  scanf("%d%d", &n, &m);
  for (int i = 1; i <= m; i++) {
    int a, b;
    scanf("%d%d", &a, &b);
    add(b, a);
  }

  for (int i = n; i; i--)
    if (!res[i]) dfs(i, i);
  
  for (int i = 1; i <= n; i++)
    printf("%d ", res[i]);
}

时间复杂度 O ( n + m ) O(n+m) O(n+m),空间 O ( n ) O(n) O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值