强联通分量

在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通。
如果有向图G的每两个顶点都强连通,则称G是一个强连通图。
非强连通图有向图的极大强连通子图,成为强连通分量(strongly connected components)。
算法流程:
用dfs遍历G中的每个顶点,通dfn[i]表示dfs时达到顶点i的时间,low[i]表示到达根的时间。
dfs跑到u的时候。
low[u ]=dfn[u]=++time.
u 入栈,扫描一遍u 所能直接达到的顶点v,如果v没有被访问过那么先dfs遍历v,再回来的时候,low[u]=min(low[v],low[u]);如果v在栈里,那么 low[u]=min(low[u],dfn[v]);
扫描完所有的v以后,如果low[u]=dfn[u]时,栈里u以及以u为根的顶点全部出栈,且刚刚出栈的就是一个极大强连通分量。

hdu 1269 联通块的数量是否为1

#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
const int N=10005;
vector<int> q[N];
int vis[N],dfn[N],low[N],cnt;
int tim,stack[N],top;
void tarjan(int u)
{
    stack[top++]=u;
    vis[u]=1;
    low[u]=dfn[u]=++tim;
    for(int i=0;i<q[u].size();i++)
    {
        int v=q[u][i];
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[v],low[u]);//孩子可以到,我也可以到
        }
        else if(vis[v])//在栈中,说明其实是祖先
            low[u]=min(low[u],dfn[v]);//能到达的祖先,所以更新。
    }
    if(low[u]==dfn[u])
    {
        cnt++;
        int v;
        do
        {
            v=stack[--top];
            vis[v]=0;
        }while(u!=v);
    }
}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)&&n+m)
    {
        for(int i=0;i<10005;i++)
            q[i].clear();
        memset(vis,0,sizeof(vis));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        tim=top=cnt=0;
        while(m--)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            q[a].push_back(b);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i])
               tarjan(i);
        puts(cnt==1?"Yes":"No");
    }
}

Tarjan算法相当于在一个有向图中找有向环,那么我们Tarjan算法中重要的作用就是缩点。缩点基于一种染色实现,我们在Dfs的过程中,尝试把属于同一个强连通分量的点都染成一个颜色,那么同一个颜色的点,就相当于一个点。
将一个有向带环图变成了一个有向无环图(DAG图)。很多算法要基于有向无环图才能进行的算法就需要使用Tarjan算法实现染色缩点,建一个DAG图然后再进行算法处理。
引入一个数组color[i]表示节点i的颜色,再引入一个数组stack[i]实现一个栈,然后在Dfs过程中每一次遇到点都将点入栈,在每一次遇到关键点的时候将栈内元素弹出,一直弹到栈顶元素是关键点的时候为止,对这些弹出来的元素进行染色即可。

hdu 2767
题意:这里有一个东西要你证明,就是有n个式子,用1到n标记,有m个关系,每个关系为a b 表示a推导出b,那么我们要这n个式子都是等价的最少还需要多少个关系。
至少加几条边让整个图变成强连通,我们根据已有的关系建图之后,强连通缩点,然后我们分别求叶子和根的数量,那么最多的那个就是我们要的答案,但是当缩点只有一个点的时候,答案是 0。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
const int N=20005;
vector<int> q[N];
int vis[N],dfn[N],low[N],stack[N],belong[N];
int top,tim,cnt;
int in[N],out[N];
#define pb push_back
void intt()
{
    memset(vis,0,sizeof(vis));
    memset(dfn,0,sizeof(dfn));
    for(int i=0;i<N;i++) q[i].clear();
    top=tim=cnt=0;
}
void tarjan(int u)
{
    stack[top++]=u;
    low[u]=dfn[u]=++tim;
    vis[u]=1;
    for(auto v:q[u])
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v]) low[u]=min(low[u],dfn[v]);
    if(low[u]==dfn[u])
    {
        cnt++;
        int v;
        do
        {
            v=stack[--top];
            vis[v]=0;
            belong[v]=cnt;
        }while(v!=u);
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        intt();
        scanf("%d%d",&n,&m);
        while(m--)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            q[a].pb(b);
        }
        for(int i=1;i<=n;i++)
              if(!dfn[i])
                  tarjan(i);
        if(cnt==1)
            puts("0");
        else
        {
            memset(in,0,sizeof(in));
            memset(out,0,sizeof(out));
            for(int i=1;i<=n;i++)
                for(auto v:q[i])
                    if(belong[i]!=belong[v])
                        out[belong[i]]++,in[belong[v]]++;
            int root=0,leaf=0;
            for(int i=1;i<=cnt;i++)
            {
                if(in[i]==0)root++;
                if(out[i]==0)leaf++;
            }
            printf("%d\n",leaf>root?leaf:root);
        }
    }
}

poj 2186
N头牛,M个关系,每个关系为 a b,表示牛a认为牛b 收欢迎,那么问根据所给信息判断有多少头牛是收到所有的牛的欢迎,而且这里a认为b受欢迎,b认为c受欢迎,那么a也会认为c受欢迎。
我们只要求叶子的个数,因为别的缩点都指向叶子,叶子处在最高层,就是最受欢迎的,那么我们根据所有缩点的out[i]=0的个数,判断,如果只有一个,那么这个缩点(强连通分量)的牛都是最受欢迎的,(这里注意,叶子数为0就是表示只有一个强连通分量,那么所有的牛都是最受欢迎的),如果有多个就输出0。
(吐槽,poj竟然不支持C++11

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
const int N=10005;
using namespace std;
vector<int> q[N];
int dfn[N]={0},vis[N]={0},low[N]={0},stack[N]={0},bel[N]={0},out[N]={0},in[N]={0};
int cnt,tim,top;
vector<int> qu[N];
void tanjar(int u)
{
    stack[top++]=u;
    vis[u]=1;
    low[u]=dfn[u]=++tim;
    for(int i=0;i<q[u].size();i++)
    {
        int v=q[u][i];
        if(!dfn[v])
        {
            tanjar(v);
            low[u]=min(low[v],low[u]);
        }
        else if(vis[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u])
    {
        cnt++;
        int v;
        do
        {
            v=stack[--top];
            vis[v]=0;
            qu[cnt].push_back(v);
            bel[v]=cnt;
        }while(v!=u);
    }
}
int main()
{
    int n,m;scanf("%d%d",&n,&m);
    while(m--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        q[a].push_back(b);
    }
    tim=cnt=top=0;
    for(int i=1;i<=n;i++)
       if(!dfn[i])
           tanjar(i);
    if(cnt==1)
        printf("%d\n",n);
    else
    {
        for(int i=1;i<=n;i++)
            for(int j=0;j<q[i].size();j++)
            {
                int v=q[i][j];
                if(bel[i]!=bel[v])
                    out[bel[i]]++;
            }
        int leaf=0,leaf_num=0;
        for(int i=1;i<=cnt;i++)
            if(out[i]==0)
                leaf++,leaf_num=i;
        if(leaf!=1) puts("0");
        else
            printf("%d\n",(int)qu[leaf_num].size());
    }
}

割点:一个结点称为割点(或者割顶)当且仅当去掉该节点极其相关的边之后的子图不连通。
桥:一条边称为桥(或者割边)当且仅当去掉该边之后的子图不连通。
在无向连通图G中,
1、根结点u为割点当且仅当它有两个或者多个子结点;
2、非根结点u为割点当且仅当u存在结点v,使得v极其所有后代都没有反向边可以连回u的祖先(u不算)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值