Tarjan算法
强连通分量(scc)
在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected component)。
Tarjan是用来求scc的
Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。
#include<cstdio>
#include<stack>
using namespace std;
stack <int> s;
int n,m,total,step;
struct E{
int next,to;
}edge[2000000];
int head[2000000],edge_num;
int belong[2000000],DFN[2000000],LOW[2000000];
bool instack[2000000];
void addedge(int x,int y){
edge[++edge_num].next=head[x];
edge[edge_num].to=y;
head[x]=edge_num;
}
void Tarjan(int x){
int i;
s.push(x);instack[x]=1;
DFN[x]=LOW[x]=++step;
for(i=head[x];i;i=edge[i].next){
if(!DFN[edge[i].to]){
Tarjan(edge[i].to);
if(LOW[x]>LOW[edge[i].to])
LOW[x]=LOW[edge[i].to];
}
if(instack[edge[i].to] && DFN[edge[i].to]<LOW[x])
LOW[x]=DFN[edge[i].to];
}
if(DFN[x]==LOW[x]){
int t;total++;
do{
t=s.top();s.pop();instack[t]=0;
belong[t]=total;
}
while(t!=x);
}
}
void solve(){
int i;
for(i=1;i<=n;i++){
if(!DFN[i])
Tarjan(i);
}
}
void out(){
int i;
for(i=1;i<=n;i++){
printf("Dot No.%d is in No.%d scc\n",i,belong[i]);
}
}
int main(){
freopen("tarjan.in","r",stdin);
int i;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++){
int f,t;
scanf("%d%d",&f,&t);
addedge(f,t);
}
solve();
out();
return 0;
}
有两个关键数组DFN、LOW
DFN[i]:第i号节点被DFS到的序号;
LOW[i]:第i号节点DFS过程中能到达的DFN值最小的节点;
所以第一次访问到时,LOW与DFN被一起赋为step值;
被DFS到的点将被放入栈中,并记录该点在栈中;
如果DFS到了一个为被访问的点,继续DFS并更新LOW值
//该点意为 本次被DFS到的点 edge[i].to
更新该点时 比较的自然是先前该点的LOW值与本次找到该点的LOW值的较小值,即
if(LOW[x]>LOW[edge[i].to])
LOW[x]=LOW[edge[i].to];
else 如果DFS到了已经在栈中的点(DFS过且不再栈中就不考虑了)
如果 该点DFN值 比 栈中的点LOW值小 更新LOW[该点]=LOW[栈中点]意思就是说找到环了,将环上的LOW值更新一下,就算到这里看起来LOW好像也并没有什么用
最后一步:退栈
这里LOW数组发挥作用
if(DFN[x]==LOW[x]){
int t;total++;
do{
t=s.top();s.pop();instack[t]=0;
belong[t]=total;
}
while(t!=x);
}
从栈顶取出一个数,直到取出的这个数是起始点(LOW[x]==DFN[x])就结束了
洛谷P2746校园网
#include<cstdio>
#include<stack>
#define MAX(a,b) a>b?a:b
using namespace std;
bool instack[2000];
int belong[2000],total,inDegree[2000],outDegree[2000];
stack<int> s;
int n,head[2000],edge_num,way,TaskA,TaskB;
struct E{
int from,next,to;
}edge[2000];
int LOW[2000],DFN[2000];
void addedge(int x,int y){
edge[++edge_num].next=head[x];
edge[edge_num].from=x;
edge[edge_num].to=y;
head[x]=edge_num;
}
void Tarjan(int x){
int i;
s.push(x);instack[x]=true;
DFN[x]=LOW[x]=++way;
for(i=head[x];i;i=edge[i].next){
if(!DFN[edge[i].to]){
Tarjan(edge[i].to);
if(LOW[edge[i].to]<LOW[x]){
LOW[x]=LOW[edge[i].to];
}
}
else if(instack[edge[i].to] && DFN[edge[i].to]<LOW[x]){
LOW[x]=DFN[edge[i].to];
}
}
if(DFN[x]==LOW[x]){
int t;
total++;
belong[x]=total;
do{
t=s.top();
belong[t]=total;
s.pop();
instack[t]=false;
}
while(t!=x);
}
}
void solve(){
int i;
for(i=1;i<=n;i++){
if(!DFN[i])
Tarjan(i);
}
for(i=1;i<=edge_num;i++){
if(belong[edge[i].from]!=belong[edge[i].to]){
inDegree[belong[edge[i].to]]++;
outDegree[belong[edge[i].from]]++;
}
}
for(i=1;i<=total;i++){
if(inDegree[i]==0)
TaskA++;
if(outDegree[i]==0)
TaskB++;
}
TaskB=MAX(TaskA,TaskB);
if(total==1){
printf("1\n0");return;
}
if(TaskA==0) TaskA=1;
printf("%d\n%d",TaskA,TaskB);
}
int main(){
scanf("%d",&n);
int i,k;
for(i=1;i<=n;i++){
scanf("%d",&k);
while(k){
addedge(i,k);
scanf("%d",&k);
}
}
solve();
return 0;
}
洛谷P2341受欢迎的牛
#include<cstdio>
#include<stack>
using namespace std;
struct edge{
int to,next,from;
}edges[500000];
int edge_num,head[500000];
int n,m;
int DFN[200000],LOW[200000];
bool instack[200000];
int Belong[200000],BMember[200000];
int tot;
int Outdegree[200000];
int steps=0,q;
stack<int> s;
void Tarjan(int x){
DFN[x]=LOW[x]=++steps;
instack[x]=1;
s.push(x);
int i;
for(i=head[x];i!=0;i=edges[i].next){
if(!DFN[edges[i].to]){
Tarjan(edges[i].to);
if(LOW[edges[i].to]<LOW[x]){
LOW[x]=LOW[edges[i].to];
}
}
else if(instack[edges[i].to]&&LOW[x]>DFN[edges[i].to]){
LOW[x]=DFN[edges[i].to];
}
}
if(DFN[x]==LOW[x]){
tot++;
do{
q=s.top();
s.pop();
Belong[q]=tot;
BMember[tot]++;
instack[q]=false;
}
while(q!=x);
}
}
void addedge(int x,int y){
edges[++edge_num].next=head[x];
edges[edge_num].to=y;
edges[edge_num].from=x;
head[x]=edge_num;
}
int main(){
scanf("%d%d",&n,&m);
int i;
for(i=1;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
addedge(a,b);
}
for(i=1;i<=n;i++){
if(!DFN[i])
Tarjan(i);
}
for(i=1;i<=m;i++){
if(Belong[edges[i].from]!=Belong[edges[i].to]){
Outdegree[Belong[edges[i].from]]++;
}
}
int zero=0,ans;
for(i=1;i<=tot;i++){
if(Outdegree[i]==0){
zero++;
ans=i;
}
}
if(zero==1) printf("%d",BMember[ans]);
else printf("0");
return 0;
}