Evanyou Blog 彩带

  题目传送门

图的遍历

题目描述

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

输入输出格式

输入格式:

 

第1 行,2 个整数 N,M 。

接下来 M行,每行2个整数 Ui,Vi ,表示边 (Ui,Vi) 。点用 1,2,,N 编号。

 

输出格式:

 

N 个整数 A(1),A(2),,A(N) 。

 

输入输出样例

输入样例#1: 复制
4 3
1 2
2 4
4 3
输出样例#1: 复制
4 4 3 4

说明

• 对于60% 的数据,1N.K103 ;

• 对于100% 的数据,1N,M105 。


分析:

  一开始看到这题想到的还是Tarjan缩点+dfs,但是还有更加巧妙的方法可以更简便地AC。

  首先分析,直接搜索肯定是有错误的,因为会有环,如果数据故意卡的话是很容易卡死的(因为遍历顺序是与存边顺序有关的,而且又是有向图,直接搜可能会导致WA掉)

  当然环可以缩点去掉。但是这样太麻烦了,不如换个思路。

  因为要求的是可以到达的编号最大的点,那么我们可以反向建边,问题就转换为求可以到达该点的最大编号的点,那么就从n到1反向遍历,直接一边dfs即可。当然要注意,该图不一定是个连通图。具体看代码。

  Code:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+7;
int n,m,head[N],size,ans[N];
struct Node{int to,next;}edge[N];
inline int read()
{
    char ch=getchar();int num=0;bool flag=false;
    while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}
    while(ch>='0'&&ch<='9'){num=num*10+ch-'0';ch=getchar();}
    return flag?-num:num;
}
inline void add(int x,int y)
{
    edge[++size].to=y;
    edge[size].next=head[x];
    head[x]=size;
}
inline void dfs(int u,int sum)
{
    if(ans[u])return;
    ans[u]=sum;
    for(int i=head[u];i!=-1;i=edge[i].next){
    dfs(edge[i].to,sum);}
}
int main()
{
    n=read();m=read();int x,y;
    memset(head,-1,sizeof(head));
    memset(ans,0,sizeof(ans));
    for(int i=1;i<=m;i++){
    x=read();y=read();add(y,x);}
    for(int i=n;i>=1;i--)
    if(!ans[i])dfs(i,i);
    for(int i=1;i<=n;i++)
    printf("%d ",ans[i]);
    return 0;
}
原文地址:https://www.cnblogs.com/cytus/p/9163116.html