Tarjan
–关于对Tarjan算法的思考与尝试
接下来(超级)详细的解释一下代码吧!
全局变量部分的解释
int i,j,n,m;
bool b[1001];
int color[1001],colorn;
int stack[1001],top;//数组模拟栈
int s,e,temp,index;
int dfn[1001],low[1001];
int head[1001];
struct data
{
int v;
data *nxt;
}a[100001];
i,j:变量。
n:点数。
m:边数。
b[]:在有向图中某个点是否经历过(布尔)。
stack[]:数组模拟栈的操作(其实就是一个数组啦~)
top:模拟栈顶,通过top–来模拟pop操作。
color[]:表示这是第几个已确定的强连通分量。
colorn:是一个累加器,记录已经确定的强连通分量个数,便于为强连通分量编号。
s,e,temp:变量。
index:累加器,是一个时间戳,来记录遍历过的节点数。
DFN[ i ] : 在DFS中该节点被搜索的次序(时间戳)
LOW[ i ] : 为i或i的子树能够追溯到的最早的栈中节点的次序号
当DFN[ i ]==LOW[ i ]时,为i或i的子树可以构成一个强连通分量。
struct内部的东西就是图论的基本功啦,如果不会请移步至链表入门。
读入优化
int r()
{
char ch=getchar();
int ans=0;
while(ch<'0'||ch>'9')
{
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
ans*=10;
ans+=ch-'0';
ch=getchar();
}
return ans;
}
插入节点
这也是基本功。如果不会可以借鉴解释 in SPFA入门。在这里不再赘述。
int ins(int ss,int ee)
{
temp++;
a[temp].nxt=&a[head[ss]];
head[ss]=temp;
a[temp].v=ee;
}
Tarjan算法
核心在这里↓↓↓
void tarjan(int x)
{
dfn[x]=++index;
low[x]=index;
b[x]=1;
stack[++top]=x;
data *p=&a[head[x]];
while(p->v!=0)
{
int vv=p->v;
if(!dfn[vv])
{
tarjan(vv);
low[x]=min(low[x],low[vv]);
}
else if(b[vv])
{
low[x]=min(low[x],dfn[vv]);
}
p=p->nxt;
}
if(dfn[x]==low[x])
{
b[x]=0;
color[x]=++colorn;
while(stack[top]!=x)
{
color[stack[top]]=colorn;
b[stack[top--]]=0;
}
top--;
}
}
下面详细解释:
tarjan算法首先是有一个类似于DFS的过程,如果不会DFS,那就……呃~~(╯﹏╰)……就请关闭此页。
首先从1号点开始深度优先搜索,每搜一个他的DFN[]=经历过的点数,即++index,LOW[]为栈中最早的与其相连的点的次序号。
当搜到某个点恰好没有出度了,就停止搜索,开始判断,如果DFN==LOW那么他自己就是一个强联通分量。
以此类推往前回退,如果对应点的下一条边不是NULL,就往下一条路走,而且不能走已经被访问过的节点。
然后如果在队列里再一次搜到栈中已有的元素,就使LOW=MIN(LOW[],DFN[])。
如果对于一个出发点x,他的终点之一vv没有被访问过,就继续深搜,这个递推过程完成后,LOW[x]=min(LOW[x],LOW[vv]),来让他们的根节点尽量小,这是我们人为设定的标准。当然了根节点向数字大的方向规定也行。
主函数
包括输入输出过程等。
int main()
{
n=r(),m=r();
for(i=1;i<=m;i++)
{
s=r(),e=r();
ins(s,e);
}
for(i=1;i<=n;i++)
if(!col[i])
tarjan(i);
for(i=1;i<=colorn;i++)
{
for(j=1;j<=n;j++)
if(color[j]==i)
cout<<j<<" ";
cout<<endl;
}
}
Tarjan的简要步骤(蒟蒻的总结)
1. 建立一个有向图,用链表存
2. 以1为起始点往后深搜至第一次搜
到头为止,每搜一次DFN时间戳与LOW
+1。
3. 如果DFN[]=LOW[],就是一个根节
点,那么他就是一个强连通分量。
4. 以此类推往前回退,如果对应点
的下一个点不是NULL,就往下一条路
走,而且不能走已经被访问过的节点
。然后如果在队列里再一次搜到下一
元素就使MIN(LOW[],DFN[])。 、
最后给大家推荐一个网页,大家可以通过例子更好的理解:
http://www.cnblogs.com/uncle-lu/p/5876729.html