强连通分量之Korasju 与tarjan两小模版题

Korasaju算法求有向图强连通分支
procedure Strongly_Connected_Components(G);
begin
1.深度优先遍历G,算出每个结点u的结束时间f[u],起点如何选择无所谓。
2.深度优先遍历G的转置图GT, 选择遍历的起点时,按照结点的结束时间从大到小进行。遍历的过程中,一边遍历, 一边给结点做分类标记,每找到一个新的起点,分类标记值就加1。
3. 第2步中产生的标记值相同的结点构成深度优先森林中的一棵树,也即一个强连通分量。
end.

poj2186.
题意比较简单就不写了。
解题思路:
1. 求出所有强连通分量
2. 每个强连通分量缩成一点,则形成一个有向无环图DAG。
3. DAG上面如果有唯一的出度为0的点,则该点能被所有的点可达。那么该点所代表的连通分量上的所有的原图中的点,都能被原图中的所有点可达,则该连通分量的点数,就是答案。
4. DAG上面如果有不止一个出度为0的点,则这些点互相不可达,原问题无解,答案为0。
缩点的时候不一定要构造新图,只要把不同强连通分量的点染不同颜色,然后考察各种颜色的点有没有连到别的颜色的边即可(即其对应的缩点后的DAG图上的点是否有出边)。
ps:
有向无环图中唯一出度为0的点,一定可以由任何点出发均可达.
(由于无环,所以从任何点出发往前走,必然终止于一个出度为0的点)
AC代码:

#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<stdio.h>
#include<string.h>
using namespace std;
vector<int>G[10001];
vector<int>rG[10002];
vector<int >vs;
bool used[10001];
int cmp[10001],a,b;
int n,m;
void dfs(int v)
{
    used[v]=true;
    for(int i=0; i<G[v].size(); i++)
        if(!used[G[v][i]])
            dfs(G[v][i]);
    vs.push_back(v);
}
void rdfs(int v,int k)
{
    used[v]=true;
    cmp[v]=k;
    for(int i=0; i<rG[v].size(); i++)
        if(!used[rG[v][i]])rdfs(rG[v][i],k);

}
int scc()
{
    memset(used,0,sizeof(used));
    vs.clear();
    for(int i=0; i<n; i++)
        if(!used[i])
            dfs(i);
    memset(used,0,sizeof(used));
    int k=0;
    for(int i=vs.size()-1; i>=0; i--)
        if(!used[vs[i]])
            rdfs(vs[i],k++);
    return k;
}

int  sloved()
{
    int nn=scc();
    int num=0;
    int u;
    for(int i=0; i<n; i++)
        if(cmp[i]==nn-1)
        {
              u=i;
            num++;
        }
        memset(used,0,sizeof(used));
        rdfs(u,0);
        for(int i=0;i<n;i++)
        {
            if(!used[i])
            {
                num=0;
                break;
            }
        }
    return num;
}

int main()
{

    scanf("%d%d",&n,&m);
    for(int i=0; i<m; i++)
    {
        scanf("%d%d",&a,&b);
        G[a-1].push_back(b-1);
        rG[b-1].push_back(a-1);
    }
    printf("%d\n",sloved());

    return 0;
}

poj1236
题目大意:N个学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输,问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。 2,至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。
问题可以转化成给定一个有向图,求:
1) 至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点
2) 至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点
ps:
有向无环图中所有入度不为0的点,一定可以由某个入度为0的点出发可达。
(由于无环,所以从任何入度不为0的点往回走,必然终止于一个入度为0的点)
AC代码:

#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<stdio.h>
#include<string.h>
#include<stack>
using namespace std;
vector< vector<int> >G;
stack<int>S;
int low[102],dfn[102],fla[102],inde[102],outde[102],cmp[102];
int in,out,cnt,a,b,time,n;
void tarjan(int s)
{
    dfn[s]=cnt++;
    low[s]=dfn[s];
    fla[s]=1;//栈内标记
    S.push(s);
    for(int i=0; i<G[s].size(); i++)
    {
        int k=G[s][i];
        if(!dfn[k])
        {
            tarjan(k);
            low[s]=min(low[s],low[k]);
        }
        else if(fla[k]&&low[s]>dfn[k])
            low[s]=dfn[k];
    }
    if(low[s]==dfn[s])
    {
        time++;
        int e;
        for(e=S.top(),S.pop(); e!=s; e=S.top(),S.pop())
        {
            cmp[e]=time;
            fla[e]=0;
        }
        cmp[e]=time;
        fla[e]=0;
    }
}
int sloved()
{
    if(time==1)
    {
        in=1;
        return 0;
    }
    for(int i=0; i<=n; i++)
        for(int j=0; j<G[i].size(); j++)
        {
            int   k=G[i][j];
            if(cmp[i]!=cmp[k])
            {
                outde[cmp[i]]++;
                inde[cmp[k]]++;
            }
        }
    for(int i=1; i<=time; i++)
    {
        if(inde[i]==0)
            in++;
        if(outde[i]==0)
            out++;
    }
    return max(in,out);
}
int main()
{

    while(scanf("%d",&n)==1)
    {
        G.clear();
        G.resize(n+1);
        for(int i=1; i<=n; i++)
            while(scanf("%d",&a)&&a)
                if(a!=i)
                    G[i].push_back(a);
        memset(low,0,sizeof(low));
        memset(dfn,0,sizeof(dfn));
        memset(fla,0,sizeof(fla));
        memset(inde,0,sizeof(inde));
        memset(outde,0,sizeof(outde));
        memset(cmp,0,sizeof(cmp));
        cnt=1,time=0,in=0,out=0;
        for(int i=1; i<=n; i++)
            if(!dfn[i])
                tarjan(i);
        int  k=sloved();
        printf("%d\n%d\n",in,k);

    }
    return 0;
}

Korasju 和tarjan复杂度都是O(v+e),但是tarjan更常用一点。

你可能感兴趣的:(Tarjan,强联通,Korasaju)