做题笔记 图的遍历 P3916 ++

今天饭后刷题,突然看见自己任务清单里面有这么一题,就打算开写。

题面说的是有向图的遍历(n <= 10^5,m <= 10^5),很自然的想到了dfs,使用vector存图,我想到了两种dfs方法:

·对于每个点进行dfs,每一次dfs遍历可以达到的所有点,求出最大值
很暴力,方法也很简单,但是问题是,最坏情况下每一个点都需要遍历全图,时间复杂度为O(nm)
·从大到小对于点进行dfs,对于每一个遍历的点,如果没有就可以把这个点的最大值设为它。
这个方法相对与上一个来说有了非常大的优化,循环从N到1,则每个点i能访问到的结点的A值都是i,每个点访问一次,这个A值就是最优的,因为之后如果再访问到这个结点那么答案肯定没当前大了。这个方法的时间复杂度O(n+m)(吧)
代码实现:

#include <cstdio>
#include <cctype>
#include <vector>
#define MAXN 100010
#define _for(i,a,b) for (int i = a;i <= b;i++)
#define _fd(i,a,b) for (int i = b;i >= a;i--)
using namespace std;
vector <int> G[MAXN];
int n,m,a[MAXN];

inline int read() {
    int a = 0,f = 1;
    char v= getchar();
    while (!isdigit(v)) {
        if (v == '-') {
            f = -1;
        }
        v = getchar();
    }
    while (isdigit(v)) {
        a = a * 10 + v - 48;
        v = getchar();
    }
    return a * f;
}

inline void addedge(int u,int v) {
    G[v].push_back(u);
}

void dfs(int x,int p) {
    if (a[x]) {
        return ;
    }
    a[x] = p;
    int vv = G[x].size();
    for (int i = 0;i < vv;i++) {
        dfs(G[x][i],p);
    }
}

int main() {
    n = read(),m = read();
    _for(i,1,m) {
        int u = read(),v = read();
        addedge(u,v);
    }
    _fd(i,1,n) {
        dfs(i,i);
    }
    _for(i,1,n) {
        printf("%d ",a[i]);
    }
    return 0;
}

后来,我翻看题解的时候,猛然发现Tarjan也能解!

使用Tarjan求出联通分量,每一个联通分量里面的点的答案都是一样的!

代码实现(题解):

#include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 
 7 const int maxn = 10e5 + 5;
 8 
 9 struct Edge{
10     int to,next;
11 }e[maxn];
12 
13 int dfn[maxn],low[maxn],Time;
14 int s[maxn],top,vis[maxn];
15 int f[maxn],n,m;
16 int cnt,belong[maxn],MAX[maxn];
17 int k,x[maxn],y[maxn],head[maxn];
18 
19 void add(int u,int v)
20 {
21     e[++k].to = v;
22     e[k].next = head[u];
23     head[u] = k;
24 }
25 
26 void tarjan(int x)                   //求有向图强联通分量的tarjan,在这里不过多叙述,想学习的可以点链接去博客qwq 
27 {
28     vis[x] = 1;
29     s[++top] = x;
30     dfn[x] = low[x] = ++Time;
31     for(int i = head[x];i;i=e[i].next)
32     {
33         int to = e[i].to;
34         if(!dfn[to])
35         {
36             tarjan(to);
37             low[x] = min(low[x],low[to]);
38         }
39         else if(vis[to])
40             low[x] = min(low[x],dfn[to]);
41     }
42     if(dfn[x] == low[x])            //如果x及其子树能够构成一个强联通分量 
43     {
44         ++cnt;
45         int j;
46         while(j=s[top])             //将其子树中的点都加入此分量中,并将此分量中的最大值保存在MAX数组中 
47         {
48             vis[j] = 0;
49             belong[j] = cnt;
50             MAX[cnt] = max(MAX[cnt],j);
51             top--;
52             if(j==x) break;
53         }
54     }
55 }
56 
57 void dfs(int x)                     //记忆化搜索 
58 {
59     if(f[x]) return;
60     f[x] = MAX[x];                  //当前强联通分量中的最大值 
61     for(int i=head[x];i;i=e[i].next)
62     {
63         int to = e[i].to;
64         if(!f[to]) dfs(to);
65         f[x] = max(f[x],f[to]);     // 子树中的最大值 
66     }
67 }
68 
69 int main()
70 {
71     //freopen("data.in","r",stdin);
72     //freopen("jkb.out","w",stdout);
73     scanf("%d%d",&n,&m);
74     for(int i=1;i<=m;i++)
75     {
76         scanf("%d%d",&x[i],&y[i]);          //保存边信息 
77         add(x[i],y[i]);
78     }
79     for(int i=1;i<=n;i++)                   //求出强联通分量 
80         if(!dfn[i])
81             tarjan(i);
82     memset(e,0,sizeof(e));                  //清空原图 
83     memset(head,0,sizeof(head));    
84     k = 0;
85     for(int i=1;i<=m;i++)                   //建立新图 
86         if(belong[x[i]]!=belong[y[i]])
87             add(belong[x[i]],belong[y[i]]);
88     for(int i=1;i<=cnt;i++)
89         if(!f[i]) dfs(i);
90     for(int i=1;i<=n;i++)                   //输出答案 
91         printf("%d ",f[belong[i]]);
92     return 0;
93 }

猜你喜欢

转载自www.cnblogs.com/doubeecat/p/10335591.html
今日推荐