关于Tarjan算法

梗概

tarjan算法有两种(我了解的),一种是用来求强连通分量的,另一种是关于割点和桥的问题。

根据机房大佬HL说过,这两种算法是互相独立的,只是代码很像。


强连通分量问题

关于这类tarjan算法,我了解到的主要的一个应用就是缩点。

例题传送门

思路

首先,如果我们考虑,如果这是一个有向无环图,我们可以用拓扑排序(DP?)的方法直接求出答案。

但是这个图是一个有向有环图,无法直接采用拓扑排序。

这时候我们想为什么无法直接用拓扑排序,因为在有环的情况下我们无法直接确定各个点的拓扑序。

如果我们把每个环都替换成一个点,点权为环中点权的总和,这就是一个有向无环图。

怎么样才可以找到这些环呢?这时候就可以拿出tarjan了。

tarjan算法

tarjan算法有两个关键的数组:dfn[]和low[]。

dfn[i]表示搜索到第i个点时的时间戳(第几个搜到的)。

low[i]表示第i个点所在的环中的所有点的最小dfn[]。

上代码:

  1. void dfs(int x){
  2. dfn[x]=low[x]=++Tt;q[++t]=x;vis[x]=1;//Tt为时间戳 q[]为栈数组
  3. //vis[i]表示i点是否在栈中
  4. for(int i=lnk[x];i;i=la[i]){
  5. int y=ne[i];
  6. if(!dfn[y]){dfs(y);low[x]=min(low[x],low[y]);}
  7. //如果该节点没被遍历过,就查看x是否与y在同一个环中。
  8. else if(vis[y])low[x]=min(low[x],dfn[y]);
  9. //否则,就查看x与y是否成环。
  10. }
  11. if(low[x]==dfn[x]){
  12. ++cnt;
  13. int y=0;
  14. while(y!=x){y=q[t];ty[y]=cnt;A[cnt]+=a[y];vis[y]=0;--t;}
  15. //这里为染色的过程,所有环都有一个颜色,后面根据每个点的颜色重新建图。
  16. }
  17. }

重新建图完毕后就可以直接进行拓扑排序。

参考程序

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int read(){
  4. int x=0,f=1;char c=getchar();
  5. while(c<'0'||c>'9'){if(c=='-')f=1;c=getchar();}
  6. while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
  7. return x*f;
  8. }
  9. int dfn[10005],low[10005],a[10005],q[100005],A[10005],ty[10005],t,tot,cnt,n,m,h,ans,Tt;
  10. int ne[100005],la[100005],lnk[10005],Ne[100005],La[100005],Lnk[10005],du[10005],dis[10005];
  11. bool vis[300005];
  12. void add(int x,int y){ne[++tot]=y;la[tot]=lnk[x];lnk[x]=tot;}
  13. void Add(int x,int y){Ne[++tot]=y;La[tot]=Lnk[x];Lnk[x]=tot;}
  14. void dfs(int x){
  15. dfn[x]=low[x]=++Tt;q[++t]=x;vis[x]=1;
  16. for(int i=lnk[x];i;i=la[i]){
  17. int y=ne[i];
  18. if(!dfn[y]){dfs(y);low[x]=min(low[x],low[y]);}
  19. else if(vis[y])low[x]=min(low[x],dfn[y]);
  20. }
  21. if(low[x]==dfn[x]){
  22. ++cnt;
  23. int y=0;
  24. while(y!=x){y=q[t];ty[y]=cnt;A[cnt]+=a[y];vis[y]=0;--t;}
  25. }
  26. }
  27. void bfs(){
  28. h=t=0;
  29. memset(vis,0,sizeof(vis));
  30. for(int i=1;i<=cnt;++i)if(du[i]==0&&(!vis[i]))q[++t]=i,dis[i]=A[i],vis[i]=1;
  31. while(h<t){
  32. int u=q[++h];
  33. for(int k=Lnk[u];k;k=La[k]){
  34. du[Ne[k]]--;
  35. if(dis[Ne[k]]<dis[u]+A[Ne[k]])dis[Ne[k]]=dis[u]+A[Ne[k]];
  36. if(du[Ne[k]]==0&&(!vis[Ne[k]]))q[++t]=Ne[k];
  37. }
  38. }
  39. for(int i=1;i<=cnt;++i)ans=max(ans,dis[i]);
  40. }
  41. int main(){
  42. n=read(),m=read();for(int i=1;i<=n;++i)a[i]=read();
  43. for(int i=1;i<=m;++i){int x=read(),y=read();add(x,y);}
  44. tot=0;for(int i=1;i<=n;++i)if(!dfn[i])dfs(i);
  45. tot=0;for(int i=1;i<=n;++i)for(int j=lnk[i];j;j=la[j])if(ty[i]!=ty[ne[j]])Add(ty[i],ty[ne[j]]),++du[ty[ne[j]]];
  46. bfs();
  47. cout<<ans;
  48. return 0;
  49. }

割点和桥问题

首先了解几个概念

割点 :在去掉这个点以及它所关联的边后,图从连通变为不连通,这个点就被称为割点。

:在去掉这条边后,图从连通变为不连通,这条边就被称为桥。

例题传送门

思路

这一类问题题目中的条件是无向图。这是和上面一种Tarjan的一个很大的区别。

以下是求割点的代码。

  1. void tar(int u,int fa){//fa作为根节点作为一个特殊的情况
  2. dfn[u]=low[u]=++cnt;int son=0;
  3. for(int j=lnk[u];j;j=la[j]){
  4. int v=ne[j];
  5. if(!dfn[v]){//找出了一个环
  6. tar(v,fa);
  7. low[u]=min(low[u],low[v]);
  8. if(low[v]>=dfn[u]&&u!=fa)cut[u]=1;//如果u的v子树和u不成环u为一个割点
  9. if(u==fa)++son;
  10. }
  11. low[u]=min(low[u],dfn[v]);
  12. }
  13. if(son>1&&u==fa)cut[fa]=1;
  14. }

接下来是求桥的代码。

  1. void tar(int u){
  2. dfn[u]=low[u]=++sum;
  3. for(int j=lnk[u];j;j=la[j]){
  4. vis[j]=1;
  5. if(!vis[j^1]){
  6. int v=ne[j];
  7. if(!dfn[v]){
  8. tar(v);
  9. low[u]=min(low[u],low[v]);
  10. if(dfn[u]<low[v])cut[j]=cut[j^1]=1;
  11. }else low[u]=min(low[u],dfn[v]);
  12. }
  13. }
  14. }

求割点和求桥的在代码上区别其实就一个等于号,如果背板子的话也比较简单。

例题参考程序:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=20005,M=200005;
  4. int read(){
  5. int x=0;char c=getchar();
  6. while(c<'0'||c>'9')c=getchar();
  7. while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
  8. return x;
  9. }
  10. int n,m,ans;
  11. int ne[M],la[M],lnk[N],tot;
  12. int dfn[N],low[N],cnt;
  13. bool cut[N];
  14. void add(int x,int y){ne[++tot]=y;la[tot]=lnk[x];lnk[x]=tot;}
  15. void tar(int u,int fa){
  16. dfn[u]=low[u]=++cnt;int son=0;
  17. for(int j=lnk[u];j;j=la[j]){
  18. int v=ne[j];
  19. if(!dfn[v]){
  20. tar(v,fa);
  21. low[u]=min(low[u],low[v]);
  22. if(low[v]>=dfn[u]&&u!=fa)cut[u]=1;
  23. if(u==fa)++son;
  24. }
  25. low[u]=min(low[u],dfn[v]);
  26. }
  27. if(son>1&&u==fa)cut[fa]=1;
  28. }
  29. int main(){
  30. n=read();m=read();
  31. for(int i=1;i<=m;++i){
  32. int x=read(),y=read();
  33. add(x,y);add(y,x);
  34. }
  35. for(int i=1;i<=n;++i)if(!dfn[i])tar(i,i);
  36. for(int i=1;i<=n;++i)if(cut[i])++ans;printf("%d\n",ans);
  37. for(int i=1;i<=n;++i)if(cut[i])printf("%d ",i);
  38. return 0;
  39. }

关于tarjan的更多相关文章

  1. HDU4738 tarjan割边|割边、割点模板

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=4738 坑点: 处理重边 图可能不连通,要输出0 若求出的结果是0,则要输出1,因为最少要派一个人 #inc ...

  2. bzoj 1179[Apio2009]Atm (tarjan+spfa)

    题目 输入 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每行一 ...

  3. tarjan讲解(用codevs1332(tarjan的裸题)讲解)

    主要借助这道比较裸的题来讲一下tarjan这种算法 tarjan是一种求解有向图强连通分量的线性时间的算法.(用dfs来实现) 如果两个顶点可以相互通达,则称两个顶点强连通.如果有向图G的每两个顶点都 ...

  4. NOIP2009最优贸易[spfa变形|tarjan 缩点 DP]

    题目描述 C 国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市.任意两个 城市之间最多只有一条道路直接相连.这 m 条道路中有一部分为单向通行的道路,一部分 为双向通行的道路 ...

  5. Tarjan

    //求强连通分量 void uni(int x,int y){ if (rank[x]<rank[y]){ fa[x]=y; size[y]+=size[x]; }else{ rank[x]+= ...

  6. 【UOJ#67】新年的毒瘤 Tarjan 割点

    #67. 新年的毒瘤 UOJ直接黏贴会炸...    还是戳这里吧: http://uoj.ac/problem/67#tab-statement Solution 看到这题的标签就进来看了一眼. 想 ...

  7. 【Codefoces487E/UOJ#30】Tourists Tarjan 点双连通分量 + 树链剖分

    E. Tourists time limit per test: 2 seconds memory limit per test: 256 megabytes input: standard inpu ...

  8. 【BZOJ-1123】BLO Tarjan 点双连通分量

    1123: [POI2008]BLO Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 970  Solved: 408[Submit][Status][ ...

  9. 【BZOJ-2730】矿场搭建 Tarjan 双连通分量

    2730: [HNOI2012]矿场搭建 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1602  Solved: 751[Submit][Statu ...

  10. Tarjan三把刀

    搞过OI的对tarjan这个人大概都不陌生.这个人发明了很多神奇的算法,在OI届广被采用. 他最广泛采用的三个算法都是和$dfn$,$low$相关的. 有向图求强连通分量 其实说直白点,就是缩点.用得 ...

随机推荐

  1. golang异常处理

    一.使用defer+recover 错误例子: [root@localhostgo_test]#cat t1.go package main func main(){ panic(1) } [root ...

  2. xml 解析参考文档

    https://www.cnblogs.com/a1656344531/archive/2012/11/28/2792863.html

  3. P1238 走迷宫

    原题链接 https://www.luogu.org/problemnew/show/P1238 为了巩固一下刚学习的广搜,练一下迷宫类型的题 不过这道题我用的深搜..... 看问题,我们就知道这道题 ...

  4. 织梦dede如何获取图集调用第一张图片完美解决方法【5.7sp2版本】

    制作产品图集多图效果的时候,常常需要获取图集第一张图片的功能,假设获取的是缩略图,那么缩略图不够大的情况下,第一展示效果就会很差,下面来介绍下如何获取调用织梦图集第一张图片的方法: 首先在 inclu ...

  5. 使用graphviz画图

    安装: 要使用Graphviz,先要在系统上安装Graphviz. 在Ubuntu上安装可以使用命令: sudo apt-get install graphviz 在其他系统安装的方法可以查看Grap ...

  6. TensorFlow windows 安装(base anaconda)

    Python conda安装之后(19年默认是python3.7) 1.降级到python3.6 2.查看python版本 3.安装tensorflow

  7. flask 基础语法学习

    回顾 #6行flask from flask import Flask app = Flask(__name__) @app.route("/") def index(): ret ...

  8. 对半导体制造(FAB)工种的全方位解析

    本文转载自微信公众号 - 感集网, 链接 https://mp.weixin.qq.com/s/MRoWRbKZFBrJcQAZPqDa7w

  9. 虚拟云主机创建多个站点方法(.htaccess实现)

    阿里的普通版虚拟云主机最多只能建一个站点,但可以绑定多个域名.如果我们想创建2个或3个主机怎么办呢?难道需要再另外购买一台主机? 其实我们可以通过.htaccess文件来定义相关域名绑定对应的网站目录 ...

  10. [算法竞赛入门经典]Message Decoding,ACM/ICPC World Finals 1991,UVa213

    Description Some message encoding schemes require that an encoded message be sent in two parts. The ...