今天饭后刷题,突然看见自己任务清单里面有这么一题,就打算开写。
题面说的是有向图的遍历(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 }