若有向图中,u和v之间可以互相到达,则称u,v是强连通的

 在有向图G中,如果两点互相可达,则称这两个点强连通,如果G中任意两点互相可达,则称G是强连通图。

定理: 1、一个有向图是强连通的,当且仅当G中有一个回路,它至少包含每个节点一次。

            2、非强连通有向图的极大强连通子图,称为强连通分量(SCC即Strongly Connected Componenet)

 

tarjan算法:

算法思想如下:

   dfn[u]表示dfs时达到顶点u的次序号(时间戳),low[u]表示以u为根节点的dfs树中次序号最小的顶点的次序号,所以当dfn[u]=low[u]时,以u为根的搜索子树上所有节点是一个强连通分量。 先将顶点u入栈,dfn[u]=low[u]=++idx,扫描u能到达的顶点v,如果v没有被访问过,则dfs(v),low[u]=min(low[u],low[v]),如果v在栈里,low[u]=min(low[u],dfn[v]),扫描完v以后,如果dfn[u]=low[u],则将u及其以上顶点出栈。

#include <cstdio>
#include <stack>
#include <cstring>
#include <iostream>
using namespace std;
int n,m,idx=0,k=1,Bcnt=0;
int head[100];
int ins[100]={0};
int dfn[100]={0},low[100]={0};
int Belong[100];
stack <int> s;
struct edge
{
    int v,next;
}e[100];
int min(int a,int b)
{
    return a<b?a:b;
}
void adde(int u,int v)
{
     e[k].v=v;
     e[k].next=head[u];
     head[u]=k++;
}
void readdata()
{
     int a,b;
     memset(head,-1,sizeof(head));
     scanf("%d%d",&n,&m);
     for(int i=1;i<=m;i++)
     {
         scanf("%d%d",&a,&b);
         adde(a,b);
     }
}
void tarjan(int u)
{
     int v;
     dfn[u]=low[u]=++idx;//每次dfs,u的次序号增加1
     s.push(u);//将u入栈
     ins[u]=1;//标记u在栈内
     for(int i=head[u];i!=-1;i=e[i].next)//访问从u出发的边
     {
         v=e[i].v;
         if(!dfn[v])//如果v没被处理过
         {
             tarjan(v);//dfs(v)
             low[u]=min(low[u],low[v]);//u点能到达的最小次序号是它自己能到达点的最小次序号和连接点v能到达点的最小次序号中较小的
         }
         else if(ins[v])low[u]=min(low[u],dfn[v]);//如果v在栈内,u点能到达的最小次序号是它自己能到达点的最小次序号和v的次序号中较小的
     }     
     if(dfn[u]==low[u])
     {
         Bcnt++;
         do
         {
             v=s.top();
             s.pop();
             ins[v]=0;
             Belong[v]=Bcnt;
         }while(u != v);
     }
}
void work()
{
     for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
     printf("\n");
     for(int i = 1;i <= 6;i++)printf("%d %d\n",dfn[i],low[i]);
     printf("共有%d强连通分量,它们是:\n",Bcnt); 
     for(int i=1;i<=Bcnt;i++)
     {
        printf("第%d个:",i);
        for(int j=1;j<=n;j++)
        {
           if(Belong[j]==i)printf("%d ",j);
        }
        printf("\n");
     }
}
int main()
{
    readdata();
    work();
    return 0;
}
/*
6 8 
1 2
1 3
2 4
3 4
3 5
4 1
4 6 
5 6
*/

 

偷偷看了别人的模板,感觉好罪恶啊

模板题 codevs 1332 上白泽慧音 http://codevs.cn/problem/1332/

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;

struct node
{
    int v,next;
}t[50010];

int tot,n,m,idx = 0,Bcnt = 0;
int dfn[50010],head[50100],ins[50010],low[50010];
int Belong[50010],g[50010];
stack<int> s;

void add(int u,int v)
{
    tot++;
    t[tot].v = v;
    t[tot].next = head[u];
    head[u] = tot;
}

void tarjan(int u)
{
    dfn[u] = low[u] = ++idx;
    s.push(u);
    ins[u] = 1;
    for(int i = head[u];i != -1; i = t[i].next)
    {
        if(!dfn[t[i].v])
        {
            tarjan(t[i].v);
            low[u] = min(low[u],low[t[i].v]);
        }
        else
            if(ins[t[i].v])
                low[u] = min(low[u],low[t[i].v]);
    }
    int v;
    if(dfn[u] == low[u])
    {
        Bcnt++;
        do
        {
            v = s.top();
            s.pop();
            ins[v] = 0;
            Belong[v] = Bcnt;
        }while(u != v);
    }
}

void work()
{
    for(int i = 1;i <= n; ++i)
        if(!dfn[i]) tarjan(i);
    int ma = 0,mi = 0;
    for(int i = 1;i <= Bcnt; ++i)
    {
        for(int j = 1;j <= n; ++j)
            if(Belong[j] == i) g[i]++;
        if(g[i] > ma)
        {
            ma = g[i];
            mi = i;
        }
    }
    printf("%d\n",ma);
    for(int i = 1;i <= n; ++i)
        if(Belong[i] == mi)
        {
            if(ma--)
            printf("%d ",i);
            else printf("%d\n",i);
        }
}

int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i = 0;i < m; ++i)
    {
        int a,b,z;
        scanf("%d%d%d",&a,&b,&z);
        add(a,b);
        if(z == 2) add(b,a);
    }
    work();
    return 0;
}

洛谷2341   https://www.luogu.org/problemnew/show/P2341

题目描述

每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶

牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果A喜

欢B,B喜欢C,那么A也喜欢C。牛栏里共有N 头奶牛,给定一些奶牛之间的爱慕关系,请你

算出有多少头奶牛可以当明星。

输入输出格式

输入格式:

 

 第一行:两个用空格分开的整数:N和M

 第二行到第M + 1行:每行两个用空格分开的整数:A和B,表示A喜欢B

 

输出格式:

 

 第一行:单独一个整数,表示明星奶牛的数量

 

输入输出样例

输入样例#1: 复制

3 3
1 2
2 1
2 3

输出样例#1: 复制

1

说明

只有 3 号奶牛可以做明星

【数据范围】

10%的数据N<=20, M<=50

30%的数据N<=1000,M<=20000

70%的数据N<=5000,M<=50000

100%的数据N<=10000,M<=50000

 

可以想到强连通分量缩点,出度为0的强连通分量的点个数,如果出度为0的强连通分量超过1个,那么就没有明星。

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
const int N = 10050;
struct node
{
    int v,next;
}t[N * 20];

int tot,n,m,idx = 0,Bcnt = 0;
int dfn[N],head[N * 20],ins[N],low[N];
int Belong[N],g[N],id[N],du[N];
stack<int> s;

void add(int u,int v)
{
    tot++;
    t[tot].v = v;
    t[tot].next = head[u];
    head[u] = tot;
}

void tarjan(int u)
{
    dfn[u] = low[u] = ++idx;
    s.push(u);
    ins[u] = 1;
    for(int i = head[u];i != -1; i = t[i].next)
    {
        if(!dfn[t[i].v])
        {
            tarjan(t[i].v);
            low[u] = min(low[u],low[t[i].v]);
        }
        else
            if(ins[t[i].v])
                low[u] = min(low[u],low[t[i].v]);
    }
    int v;
    if(dfn[u] == low[u])
    {
        Bcnt++;
        do
        {
            v = s.top();
            s.pop();
            ins[v] = 0;
            Belong[v] = Bcnt;
            id[v] = Bcnt;
            g[Bcnt]++;
        }while(u != v);
    }
}

void work()
{
    for(int i = 1;i <= n; ++i)
        if(!dfn[i]) tarjan(i);
    for(int i = 1;i <= n; ++i)
    {
        for(int j = head[i];j != -1; j = t[j].next)
        {
            if(id[i] != id[t[j].v])
                du[id[i]]++;
        }
    }
    int tt = 0;
    for(int i = 1;i <= Bcnt; ++i)
    {
        if(!du[i])
        {
            if(tt)
            {
                printf("0\n");
                return;
            }
            tt = i;
        }
    }
    printf("%d\n",g[tt]);
}

int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i = 0;i < m; ++i)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
    }
    work();
    return 0;
}

POJ1236

结论: 一个有向无环图变成强连通图,最少加边max(a,b), a代表入度为0的点,b代表出度为0的点。

缩点+记录度

#include<iostream>
#include<cstdio>
#include<map>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
using namespace std;

struct node
{
    int v,next;
}t[100000];
int belong[1100],head[1100],dout[1100],din[1100],low[1100],dfn[1100];
stack<int>s;
bool vis[1100];
int tot,x,n,ans,ans1,ans2,idx;

void add(int u,int v)
{
    t[++tot].v = v;
    t[tot].next = head[u];
    head[u] = tot;
}

void tarjan(int u)
{
    low[u] = dfn[u] = ++idx;
    s.push(u);
    vis[u] = 1;
    int v;
    for(int i = head[u];i != -1;i = t[i].next)
    {
        v = t[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            if(low[u] > low[v]) low[u] = low[v];
        }
        else
            if(vis[v] && low[u] > low[v]) low[u] = low[v];
    }
    if(low[u] == dfn[u])
    {
        ans++;
        do
        {
            v = s.top();
            s.pop();
            vis[v] = 0;
            belong[v] = ans;
        }while(u != v);
    }
}

int main()
{
    scanf("%d",&n);
    tot = 0;
    memset(head,-1,sizeof(head));
    for(int i = 1;i <= n; ++i)
    {
        while(1)
        {
            scanf("%d",&x);
            if(!x) break;
            add(i,x);
        }
    }
    idx = ans = 0;
    for(int i = 1;i <= n;++i)
        if(!dfn[i]) tarjan(i);
    for(int i = 1;i <= n; ++i)
    {
        for(int j = head[i];j != -1;j = t[j].next)
        {
            int k = t[j].v;
            if(belong[k] != belong[i])
            {
                din[belong[k]]++;
                dout[belong[i]]++;
            }
        }

    }
    ans1 = ans2 = 0;
    for(int i = 1;i <= ans; ++i)
    {
        if(!din[i]) ans1++;
        if(!dout[i]) ans2++;
    }
    ans2 = max(ans1,ans2);
    if(ans == 1) printf("1\n0\n");
    else printf("%d\n%d\n",ans1,ans2);
    return 0;
}