前几天想要写的子糊串咕咕了,姑且从图开始吧;
Tarjan
首先来购买一些数组:
dfn【i】 :i点的dfs序 (即dfs搜到i点时已经过的点数)
low【i】:i点所在的子树dfs序最小的点(可以认为是极浅的一个点)
num :dfs序计数(这是什么时候混进来的?)
可它们是空的~~~~;
但不妨开始我们的递归之旅;
看图(图是到处都可以找得到的)
看到那个1号点了吗,从他开始;
于是有了一小段伪代码
dfs(1)
{
dfn【1】=low【1】=++num;|| 初始化 默认自己为全子树的根(但人家本来就是啊啊啊)
stack【++top】=1;|| 手动入栈
for(可以去的点v(2 ,4))
{
dfs(v);|| 下一个
|| 更新总是在回溯的时候
low【1】=min(low【2】,low【1】);|| 最浅的当然还是low【1】=1了
}
top-- ||出栈
}
如此简单,
如果只考虑一个点的图当然简单了;
还有子节点,还要输出强连通分量,何以敌?
那不就完惹QAQ。。。(此文何用。。。?)
于是有了其他点的一小段伪代码
dfs(cur)
{
dfn【cur】=low【cur】=++num;
cur进入stack
for(能到的点 v )
{
if(!dfn【v】)|| 深搜未到
{
dfs(v);
low【cur】=min (low【cur】 , low【v】 );
}
if(dfn 【v】 && in-stack【v】) || 深搜到过 , 还在栈中 , ==》 在同一个连通分量中 ==》 需要确定更浅的
{
low 【cur】 = min (low 【cur】,dfn【v】)
}
}
if(dfn【cur】==low【cur】)|| 更新后未被更小点取代,必定为某分量的顶点
{
while(cur!=stack。top)
printf stack。top || 打印 《》 染色|| color【stack。top】=totcolor
top 出栈
}|| totcolor++
cur出stack; ||
}
就上面那张tarjan入门必看图
我们模拟一下运行过程 (抖癌晚期就不献图了)
上一个表格
cur | dfn | low | stack | ||
1 | 100000 | 100000 | +1 | ||
2 | 120000 | 120000 | 1+2 | ||
3 | 123000 | 123000 | 12+3 | ||
6 | 123004 | 123004 | 123 +6-6 | ||
3 | 123004 | 123004 | 123-3 | ||
5 | 123054 | 123054 | 12+5 | ||
1(其实未到) | 123054 | 123014 | 125 | ||
2 | 123054 | 113014 | 125 | ||
1 | 123054 | 113014 | 125 | ||
4 | 123654 | 113614 | 125+4 | ||
1 | 123654 | 113114 | 1254-4-5-2-1 |
特别说明2*一个令蒟蒻我蒙很久的地方:
从5号点望向(因为不能到QAQ)1号点时
失散多年的父子相见了
根据上文伪代码
low【5】= min(low【5】,dfn【1】)= 1 ==》 5在一个以1为顶点的子树
low【5】!=dfn【5】 ==》 5 不出栈,回到2,
low【2】= min(low【2】,low【5】)= 1 ==》 2发现跟着5跟着1混更能体现自身价值,于是进入一个以1为顶点的子树
目前栈中 : 1 2 5;
low【2】!=dfn【2】 ==》 2不出栈,回到1;
1 还可以到4
4 没走过 ==》 入栈
dfn【4】=low【4】=6;
看见5,还在栈中,不能去;
low【4】= min (low【4】,dfn【5】 )=5;
low【4】!= dfn【4】 ==》4 不出栈,回到1;
dfn【1】==low【1】 ( <-> _ <-> !)
都出去吧;
于是栈空,于是强连通分量求完;
这就完了?不可能的;
图不联通怎么破;
for(int i=1;i<=n; ++i)
{
if ( ! dfn[i] ) dfs (i ); || 其实dfn 也可以 换作 low ,要是不嫌弃开个bool数组记录也行的哟
}
————————
这里口胡一下为什么low i ==dfn i 时出栈能保证极大
当其子树 dfn < dfn i 时才可以使 low i ==dfn i ;不然就更新了;
子树dfs序当然会比根小了;
也有些时候他的儿子与他的父亲暗地里勾结了
此时则得到一个SCC,直接回溯不弹出,是防止有其他儿子勾搭上了更老的父亲
————————
来个又水又裸的题
[USACO06JAN]牛的舞会The Cow Prom
#include<bits/stdc++.h> using namespace std; struct VVV{ int TO,NE; }bian[50100]; int Head[10100],dfn[10100],low[10100],stac[10100]; bool instac[10100]; void addd(int u,int v,int cnt) { bian[cnt].NE=Head[u];bian[cnt].TO=v;Head[u]=cnt; } int num=0,top=0,sum=0; void tar(int root) { // cout<<"YOL:"<<root<<endl; // for(int i=1;i<=top;++i) // { // cout<<stac[i]<<" "; // }cout<<endl; stac[++top]=root; instac[root]=1; dfn[root]=low[root]=++num; for(int i=Head[root];i;i=bian[i].NE) { int v=bian[i].TO;//cout<<" VV: "<<v<<" "; if(!dfn[v]) { tar(v); low[root]=min(low[root],low[v]); } else if(instac[root]) { low[root]=min(low[root],dfn[v]); } } if(dfn[root]==low[root]) { int _top=top; while(stac[top]!=root) { instac[stac[top]]=0;top--; } instac[root]=0; top--; if(_top-top>1)sum++; } } int main() { int n,m,x,y; scanf("%d%d",&n,&m); for(int i=1;i<=m;++i) { scanf("%d%d",&x,&y); addd(x,y,i); } for(int i=1;i<=n;++i) { if(!dfn[i]) { tar(i); } } printf("%d",sum); // for(int i=1;i<=n;++i) // { // cout<<dfn[i]<<" "<<low[i]<<endl; // } return 0; }
这可是老爷子成名之作,有不少大大的用处;
还可以有桥,割顶,和a series of 玄学优化
想(xue)更(hui)了再写吧。。。