今天饭后刷题,突然看见自己任务清单里面有这么一题,就打算开写。
题面说的是有向图的遍历(n <= 10^5,m <= 10^5),很自然的想到了dfs,使用vector存图,我想到了两种dfs方法:
- 对于每个点进行dfs,每一次dfs遍历可以达到的所有点,求出最大值
- 很暴力,方法也很简单,但是问题是,最坏情况下每一个点都需要遍历全图,时间复杂度为O(nm)
- 从大到小对于点进行dfs,对于每一个遍历的点,如果没有就可以把这个点的最大值设为它。
- 这个方法相对与上一个来说有了非常大的优化,循环从N到1,则每个点i能访问到的结点的A值都是i,每个点访问一次,这个A值就是最优的,因为之后如果再访问到这个结点那么答案肯定没当前大了。这个方法的时间复杂度O(n+m)(吧)
代码实现:
1 #include <cstdio> 2 #include <cctype> 3 #include <vector> 4 #define MAXN 100010 5 #define _for(i,a,b) for (int i = a;i <= b;i++) 6 #define _fd(i,a,b) for (int i = b;i >= a;i--) 7 using namespace std; 8 9 vector <int> G[MAXN]; 10 int n,m,a[MAXN]; 11 12 inline int read() { 13 int a = 0,f = 1; 14 char v= getchar(); 15 while (!isdigit(v)) { 16 if (v == '-') { 17 f = -1; 18 } 19 v = getchar(); 20 } 21 while (isdigit(v)) { 22 a = a * 10 + v - 48; 23 v = getchar(); 24 } 25 return a * f; 26 } 27 28 inline void addedge(int u,int v) { 29 G[v].push_back(u); 30 } 31 32 void dfs(int x,int p) { 33 if (a[x]) { 34 return ; 35 } 36 a[x] = p; 37 int vv = G[x].size(); 38 for (int i = 0;i < vv;i++) { 39 dfs(G[x][i],p); 40 } 41 } 42 43 int main() { 44 n = read(),m = read(); 45 _for(i,1,m) { 46 int u = read(),v = read(); 47 addedge(u,v); 48 } 49 _fd(i,1,n) { 50 dfs(i,i); 51 } 52 _for(i,1,n) { 53 printf("%d ",a[i]); 54 } 55 return 0; 56 }
后来,我翻看题解的时候,猛然发现Tarjan也能解!
使用Tarjan求出联通分量,每一个联通分量里面的点的答案都是一样的!
代码实现(题解):
1 #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 }