What is 强联通分量?

让我们举个栗子

好吧其实不是这个
来我们再举个例子

强联通分量的定义就是,在有向图一组点中,如果每两个点可以互相到达,那么我们叫这组点是一个强联通分量(Strong Connected Components,简称scc)

比如说我们看上面这个图,第一个中的1,2,3,4就是一个强联通分量,因为他们任意两个点都可以互相到达,但是第二个就不是,因为4号点不能到达其他三个任意的点

算法实现

讲完了定义,我们来看看如何来实现
主要的算法是kosaraju和tarjan,主要用的是tarjan,kosaraju我之前学过,但是因为代码实在有点长,加上好久没写,就忘了…
之前写过一篇洛谷p1726 上白泽慧音 kosaraju的题解
这里还是主要讲tarjan吧
这里介绍几个数组
dfn[x] ——————- x点被访问的时间
low[x] —————— 从x点出发能够到达的最小的dfn值得点的dfn
stack[x] ————----栈,这里建议手写,用STL的可能会爆栈
color[x] ——————x点位于第几个scc,位于同一个scc的点的color值相同
vis[x]——————- 记录x点在不在栈里,如果在是1

我们在外面写一个循环,访问每一个没有访问过的节点(dfn[i]=0),每次我们对当前节点的可以到达的节点进行拓展,同时更新low的值,接下来我们需要判断,如果当前节点的dfn和low的值相等,那么我们在这个scc中的拓展已经结束了,那么我们就把栈里面的所有和当前节点的不同的点弹出,直到当前节点(含)

这个东西,说实话,真的是不太好理解…

如果大家不理解的话可以先把代码背下来,然后去,慢慢理解去,打熟练了之后慢慢就理解了(like me)

下面上代码吧

int n,m,w[N],v[N];
int dfn[N],low[N],dfsxu,stack[N],top,color[N],sum;inline void tarjan(int u){dfn[u]=++dfsxu;low[u]=dfsxu;vis[u]=true;stack[++top]=u;for(int i=head1[u];~i;i=e1[i].next){int v=e1[i].to;if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);}else if(vis[v]) low[u]=min(low[u],low[v]);}if(dfn[u]==low[u]){color[u]=++sum;vis[u]=false;while(stack[top]!=u){color[stack[top]]=sum;vis[stack[top--]]=false;}top--;}
}

tarjan其实是有很多用的,比如说缩点,就是说,根据题目的条件,我们可以把一个scc缩成一个超级点,这样可以从很大程度上节省我们的时间

比如说这一道题

USACO15JAN Grass Cownoisseur

题目描述

In an effort to better manage the grazing patterns of his cows, Farmer John has installed one-way cow paths all over his farm. The farm consists of N fields, conveniently numbered 1…N, with each one-way cow path connecting a pair of fields. For example, if a path connects from field X to field Y, then cows are allowed to travel from X to Y but not from Y to X.

Bessie the cow, as we all know, enjoys eating grass from as many fields as possible. She always starts in field 1 at the beginning of the day and visits a sequence of fields, returning to field 1 at the end of the day. She tries to maximize the number of distinct fields along her route, since she gets to eat the grass in each one (if she visits a field multiple times, she only eats the grass there once).

As one might imagine, Bessie is not particularly happy about the one-way restriction on FJ’s paths, since this will likely reduce the number of distinct fields she can possibly visit along her daily route. She wonders how much grass she will be able to eat if she breaks the rules and follows up to one path in the wrong direction. Please compute the maximum number of distinct fields she can visit along a route starting and ending at field 1, where she can follow up to one path along the route in the wrong direction. Bessie can only travel backwards at most once in her journey. In particular, she cannot even take the same path backwards twice.

约翰有n块草场,编号1到n,这些草场由若干条单行道相连。奶牛贝西是美味牧草的鉴赏家,她想到达尽可能多的草场去品尝牧草。

贝西总是从1号草场出发,最后回到1号草场。她想经过尽可能多的草场,贝西在通一个草场只吃一次草,所以一个草场可以经过多次。因为草场是单行道连接,这给贝西的品鉴工作带来了很大的不便,贝西想偷偷逆向行走一次,但最多只能有一次逆行。问,贝西最多能吃到多少个草场的牧草。

首先看到这道题,我们应该先缩点,因为在一个强连通分量中的点可以互相到达,逆行就没有意义了,所以我们缩点之后,我们用dis1[i]表示从1到i的最长路(边权是道路出发点的点权),然后我们建一张反图,用dis2[i]表示从i到1的最长路
上述过程可以通过SPFA实现
那么我们的答案就是
ans=max(ans,dis1[u]+dis2[v]−size[color[1]])(e(u,v)∈map3(反图))ans=max(ans,dis1[u]+dis2[v]-size[color[1]]) (e(u,v)\in map3(反图)) ans=max(ans,dis1[u]+dis2[v]−size[color[1]])(e(u,v)∈map3(反图))
代码:

# include <cstdio>
# include <algorithm>
# include <cstring>
# include <cmath>
# include <climits>
# include <iostream>
# include <string>
# include <queue>
# include <vector>
# include <set>
# include <map>
# include <cstdlib>
//# include <stack>
# include <ctime>
using namespace std;# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define mct(a,b) memset(a,b,sizeof(a))
# define gc getchar()
typedef long long ll;
const int N=1e5+5;
const int inf=0x7fffffff;
const double eps=1e-7;
inline int read(){int s=0,w=1;char c=gc;while(c<'0'||c>'9'){if(c=='-')w=-1;c=gc;}while(c>='0'&&c<='9')s=s*10+c-'0',c=gc;return s*w;
}
inline void write(int x){if(x<0) putchar('-'),x=-x;if(x>9) write(x/10);putchar(x%10+'0');
}
int n,m;
int head1[N],head2[N],head3[N],cnt1,cnt2,cnt3;
int dfn[N],low[N],stack[N],dfsxu,top,sum,color[N];
int size[N];
int dis1[N],dis2[N],ans;
bool vis[N];struct Edge{int to,next;
}e1[N],e2[N],e3[N];inline void add1(int x,int y){e1[++cnt1]=(Edge){y,head1[x]},head1[x]=cnt1;
}inline void add2(int x,int y){e2[++cnt2]=(Edge){y,head2[x]},head2[x]=cnt2;
}inline void add3(int x,int y){e3[++cnt3]=(Edge){y,head3[x]},head3[x]=cnt3;
}inline void tarjan(int u){dfn[u]=++dfsxu;low[u]=dfsxu;vis[u]=true;stack[++top]=u;for(int i=head1[u];~i;i=e1[i].next){int v=e1[i].to;if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);}else if(vis[v]) low[u]=min(low[u],low[v]);}if(dfn[u]==low[u]){color[u]=++sum;vis[u]=false;while(stack[top]!=u){color[stack[top]]=sum;vis[stack[top--]]=false;}top--;}
}inline void spfa1(int src){dis1[src]=size[src];queue<int> q;vis[src]=true;q.push(src);while(!q.empty()){int u=q.front();q.pop();for(int i=head2[u];~i;i=e2[i].next){int v=e2[i].to;if(dis1[v]<dis1[u]+size[v]){dis1[v]=dis1[u]+size[v];if(!vis[v]) q.push(v),vis[v]=true;}}vis[u]=false;}
}inline void spfa2(int src){dis2[src]=size[src];queue<int> q;vis[src]=true;q.push(src);while(!q.empty()){int u=q.front();q.pop();for(int i=head3[u];~i;i=e3[i].next){int v=e3[i].to; if(dis2[v]<dis2[u]+size[v]){dis2[v]=dis2[u]+size[v];if(!vis[v]) q.push(v),vis[v]=true;}}vis[u]=false;}
}int main(){mct(head1,-1);mct(head2,-1);mct(head3,-1);n=read(),m=read();Rep(i,1,m){int u=read(),v=read();add1(u,v);}mct(vis,0);Rep(i,1,n) if(!dfn[i]) tarjan(i);Rep(i,1,n) size[color[i]]++;Rep(u,1,n)for(int i=head1[u];~i;i=e1[i].next){int v=e1[i].to;if(color[u]!=color[v]){add2(color[u],color[v]),add3(color[v],color[u]);} }mct(vis,0);spfa1(color[1]);mct(vis,0);spfa2(color[1]);ans=size[color[1]];Rep(u,1,n)for(int i=head3[color[u]];~i;i=e3[i].next)if(dis1[color[u]]>0&&dis2[e3[i].to]>0) ans=max(ans,dis1[color[u]]+dis2[e3[i].to]-size[color[1]]);write(ans); puts("");return 0;
}

强联通分量:Tarjan缩点相关推荐

  1. [vios1023]维多利亚的舞会3强联通分量tarjan

    题目链接:https://vijos.org/p/1023 最近在练强联通分量,当然学的是tarjan算法 而这一道题虽然打着难度为3,且是tarjan算法的裸题出没在vijos里面 但其实并不是纯粹 ...

  2. [HDOJ6165] FFF at Valentine(强联通分量,缩点,拓扑排序)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6165 题意:问一个有向图中是否有任意两点可以到达. 读错题就彻底输了,读成判断是否有任意条路,使得经过 ...

  3. hoj 3005 Game Rigging 强联通分量求缩点

    /* 题目: 现给出各位选手的能力比较并给出自己的朋友的参赛号码,如何组织比赛使得自己的朋友能够获胜 分析: 各选手能力比较可以构造一个有向图,而想要使得自己的朋友要赢得比赛,所以他的所在的连通块 必 ...

  4. P3387-【模板】缩点【tarjan,强联通分量,DAGdp】

    正题 评测记录: https://www.luogu.org/recordnew/lists?uid=52918&pid=P3387 大意 一个有向图.每个点有权值,但每个值只能取一次,每条边 ...

  5. Tarjan的强联通分量

    求强联通分量有很多种. <C++信息学奥赛一本通>  中讲过一个dfs求强联通分量的算法Kosdaraju,为了骗字数我就待会简单的说说.然而我们这篇文章的主体是Tarjan,所以我肯定说 ...

  6. [图论]强联通分量+缩点 Summer Holiday

    Summer Holiday Description 听说lcy帮大家预定了新马泰7日游,Wiskey真是高兴的夜不能寐啊,他想着得快点把这消息告诉大家,虽然他手上有所有人的联系方式,但是一个一个联系 ...

  7. POJ 2186 popular cow 有向图的强联通问题 Tarjan算法

    参考:http://hi.baidu.com/1093782566/blog/item/e5a0e9229913bd048b82a175.html http://www.cppblog.com/Iro ...

  8. 强联通块tarjan算法

    http://poj.org/problem?id=1236 第一问:需要几个学校存在软件,才能通过传递,使得所有的学校都有软件 用tarjan算法求出强联通分量后,将每个联通分量缩成一个点,那么问题 ...

  9. 强联通分量与双连通分量

    强联通分量 1.概念 在有向图G中,如果两点互相可达,则称这两个点强连通,如果G中任意两点互相可达,则称G是强连通图. 定理: 1.一个有向图是强连通的,当且仅当G中有一个回路,它至少包含每个节点一次 ...

  10. 关于强联通分量 的求法讨论

    这个讨论主要是关于 HA2006年最受欢迎的牛 的讨论 . 尽管这道题对于很多dalao来说都觉得是模板题,但是仍是值得思考的,因为我第一次写这道题的时候, 缩完点之后建图建错玄学跑dfs n^2做法 ...

最新文章

  1. 2021年大数据ELK(二十七):数据可视化(Visualize)
  2. JFrame 简单使用
  3. 温度传感器有源电子标签设计浅析
  4. ArrayList 扩容
  5. div设置百分比高度 宽度
  6. Oracle区概念总结
  7. python和按键精灵自动化测试_按键精灵对APP自动化测试(下)
  8. python字符串find_Python字符串| 带示例的find()方法
  9. Shutdown Abort :亚马逊成功迁移物流业务中最后一个Oracle数据库
  10. 数据结构与算法基础02:线性表
  11. 网页特效代码大全网址
  12. Linux替代Windows系统软件比拼
  13. 机器人改变生活利弊英语作文_左手建筑右手餐饮,机器人如何改变大众生活? 圆梦人感言...
  14. CentOS 7 samba 配置
  15. linux驱动调试方法
  16. Lightbend就收购OpsClarity一事与InfoQ的对话
  17. 杭电 2544 最短路(bellman详解)
  18. 暄桐林曦老师浅谈“如何加强专注自律”
  19. 排序算法大比拼(2.1)——时间O(n log2n)篇之归并排序
  20. js实现上下左右移动小方块

热门文章

  1. gs地图开发_中国地图全图高清版大图审图号GS(2016)2923号
  2. c花体复制_可复制花体字大全?
  3. 中国企业云计算应用现状及需求调研报告
  4. win7计算机怎么优化驱动器,win7电脑怎么优化
  5. 如何使用视频剪辑软件将qsv格式视频转换为MP4格式
  6. 小学计算机教案设计ppt,小学信息技术制作幻灯片的教案
  7. c 教程网 linux,Linux基本命令C教程网cppcoursecom.ppt
  8. APP瘦身之资源优化篇
  9. JAVA 异或校验byte数组
  10. 从零开始之驱动发开、linux驱动(二十、linux设备驱动中的并发控制)