作业

tarjan算法与强连通图

  1. 强连通分量
    是指有向图G里顶点间能互相到达的子图
  2. 极大强连通分量
    如果一个强连通分量没有被其它强通分量完全包含
  3. Tarjan算法
    求解有向图强连通分量的线性时间的算法
运用Tarjan算法需要有DFS、栈、树、图基础= =、

Tarjan算法就是对图DFS,每个强连通分量为搜索树中的一棵子树。把当前搜索树中未处理的节点加入一个堆栈中,回溯时判断栈顶到栈中的节点是否为一个强连通分量。

原理

1.强连通分量一定是有向图的搜索树的子树!所以也可以说在DFS中,同一分量的顶点(所有),都在同一棵DFS树中。
2.子树元素在栈里是相邻的,而且根节点应该在所有子树元素下面。
3.当一个点同时属于两个不同强连接子图中时,该点是图一∪图二中的点。
4.遍历整个树后若某点DFN[ ]==Low[ ];则该点是该树的子树的根。从它以上直到栈顶的所有元素组成一个强连通的分量。(DFN数组记录搜索到该点的时间,Low数组是一个标记数组(功能你猜啊~))

算法

具体算法步骤
1.数组的初始化:当首次搜索到点p时,Dfn与Low数组的值都为到该点的时间。
2.堆栈:每搜索到一个点,将它压入栈顶。
当点p有与点p’相连时,如果此时(时间为dfn[p]时)p’不在栈中,p的low值为两点的low值中较小的一个。
3.当点p有与点p’相连时,如果此时(时间为dfn[p]时)p’在栈中,p的low值为p的low值和p’的dfn值中较小的一个。
4.每当搜索到一个点经过以上操作后(也就是子树已经全部遍历)的low值等于dfn值,则将它以及在它之上的元素弹出栈。这些出栈的元素组成一个强连通分量。
5.继续搜索(或许会更换搜索的起点,因为整个有向图可能分为两个不连通的部分),直到所有点被遍历。
对了tarjan时间复杂度O(m+n),n,m为点和条件数。
伪代码~~~
tarjan(u)
{
DFN[u]=Low[u]=++Index //为节点u设定次序编号和Low初值
Stack.push(u) //将节点u压入栈中
for each (u, v) in E //枚举每一条边
if (v is not visted) //如果节点v未被访问过
tarjan(v) //继续向下找
Low[u] = min(Low[u], Low[v])
else if (v in S) //如果节点v还在栈内
Low[u] = min(Low[u], DFN[v])
if (DFN[u] == Low[u]) //如果节点u是强连通分量的根
repeat
v = S.pop //将v退栈,为该强连通分量中一个顶点
print v
until (u== v)
}

~~~~~~~~~~~~~~

例子

POJ 1236Network of Schools

描述
有一些学校连到了一个电脑网络中。这些学校达成了一个协议:每个学校保存一个其他学校名字的列表,列出它发送软件能到达的学校(即接收学校)。注意:如果B在A学校的发送列表中,那么A不一定在B学校的列表中。你需要写一个程序,计算最小的学校数量,这些学校接收新软件后,能让所有的学校在网络内能接收到这个软件(子任务一)。作为一个额外的任务,我们想要保证通过传输新软件到一个任意的学校能使这个软件能在网络中送达到所有的学校。为了达到这个目标,我们可能需要拓展新成员到学校的接收列表。计算这个需要拓展的最小数量,使得无论我们发送新软件到哪个学校,它都可以到达任意一个学校(子任务二)。一个拓展意味着增加一个新成员到某一个学校的接收列表中。

输入
第一行包括一个整数N:网络中的学校数量(2≤N≤100)。1~N每个数字分别代表一个学校。每个接下来的N行描述了一个接收者列表。第i+1行包括了学校i的接收列表。每个列表以0结束。一个空列表在一行中只有一个单独的0.

输出
你的程序需要写两行作为标准输出。第一行需要包括一个正整数:子任务一的答案。第二行需要包括子任务二的答案。

样例输入
5
2 4 3 0
4 5 0
0
0
1 0

样例输出
1
2
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1.图存
2.遍历{
3.堆栈
4.判断
5.输出
}
真就这样就好了= =。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

代码如下~
#include <string.h>
#include <stdio.h>
#define V   105
#define E   100500
struct edge{
    int to,next;
}Edge[E];
int head[V],e,n;
int indeg[V],outdeg[V];
int belong[V],low[V],dfn[V],scc,cnt;
int S[V],top;
bool vis[V];
int addedge(int u,int v){
    Edge[e].to=v;
    Edge[e].next=head[u];
    head[u]=e++;
    return 0;
}
void tarjan(int u){
    int v;
    dfn[u]=low[u]=++cnt;
    S[top++]=u;
    vis[u]=true;
    for(int i=head[u];i!=-1;i=Edge[i].next){
        v=Edge[i].to;
        if(dfn[v]==0){
            tarjan(v);
            low[u]=low[u]<low[v]?low[u]:low[v];
        }
        else if(vis[v]&&low[u]>dfn[v])
            low[u]=dfn[v];
    }
    if(dfn[u]==low[u]){
        ++scc;
        do{
            v=S[--top];
            vis[v]=false;
            belong[v]=scc;
        } while(u!=v);
    }
}
int solve(){
    scc=top=cnt=0;
    memset(dfn,0,sizeof(dfn));
    memset(vis,false,sizeof(vis));
    for(int u=1;u<=n;++u)
        if(dfn[u]==0)
            tarjan(u);
    return scc;
}
void count_deg(){
    memset(indeg,0,sizeof(indeg));
    memset(outdeg,0,sizeof(outdeg));
    for(int u=1;u<=n;++u)
        for(int i=head[u];i!=-1;i=Edge[i].next){
            int v=Edge[i].to;
            if(belong[u]!=belong[v]){
                indeg[belong[v]]++;
                outdeg[belong[u]]++;
            }
        }
}
int main(){
    int u,v,i;
    scanf("%d",&n);
    e=0;
    memset(head,-1,sizeof(head));
    for(u=1;u<=n;++u)
        while(scanf("%d",&v)&&v!=0)
            addedge(u,v);
    solve();
    if(scc==1)
        printf("1\n0\n");
    else{
        count_deg();
        int inc=0,outc=0;
        for(i=1;i<=scc;++i){
            if(indeg[i]==0)
                inc++;
            if(outdeg[i]==0)
                outc++;
        }
        printf("%d\n%d\n",inc,(inc>outc?inc:outc));
    }
    return 0;
}

多谢补充(///^///);

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值