关于对Tarjan算法的思考与尝试

来源:互联网 发布:幻想神域攻略软件 编辑:程序博客网 时间:2024/06/04 19:19

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
这里写图片描述

原创粉丝点击