图 求强连通分支子图

  强连通分支,

在有向图中,如果任意两个点能够互相联系,那么这就称为强连通分支;

给定一个有向图,它不一定是强连通的,但一定可以分为多个强连通分支,这里说明怎样求得一个有向图中最少的强连通分支。

还是应该从dfs下手;

每个强连通分支中,都有一个点是dfs中子树的根;也就是说,每个强连通分支它中的节点都在一个树中,而出来树这个条件,强连通分支应该还有其他的条件:

为每个节点设置一个dfn(表示在dfn中每个节点的遍历顺序),一个lowint(表示一个节点中它本身或者他的子节点或者他的在树的范围内的后向弧以及横跨弧的最小值)。

如果point[i],dfn>point[i].lowint,那么就表示这个点的强连通分量的根应该在其遍历过的前面的点中。

也就是说,如果一个点的dfn=lowint,那么这个点之下的树中的节点就是一个强连通分量;

 

具体算法:

每个点的初始dfn以及lowint都为0,i=1;

1 从某个点开始dfs;

2 U.dfs=i;U.lowint=i;U入栈

3 判断U是否有边与其相连;如果有,向下;如果没有,则跳至步骤7;

4 相连的点位V;判断V是否已经遍历过,如果没有,那么father[V]=U;U<-V;返回2;

5 如果V已经遍历过,则判断V是否在栈中,且V.dfn是否小于U.dfn;如果成立,则向下,不成立,则返回3;

6 U.lowint=min(U.lowint,V.dfn);返回3;

7 如果U.dfn==U.lowint,则说明U以下的节点都是强连通图,将栈中的节点出栈至U;

8 如果father[U]存在,则father[U].lowint=min(father[U].lowint,U.lowint);U=father[U],返回步骤3;

9 如果不存在,则判断点集中是否还存在dfn为0的点,如果不存在,则结束;如果存在,则将其赋值给U返回2;

 

 

代码(还没有找到题目来验证,不过自己的数据都通过了,欢迎找茬~):

 

#include<iostream>
using namespace std;
struct node{
       int dfn;
       int lowint;
       int instack;
       };
int ver[100][100];
node point[100];
int stack[100];
bool flag[100][100];
int father[100];
int dfnstart;
int times;
int n,m;
int min(int a1,int b1){
    if(a1<b1)
        return a1;
    return b1;
}
void outstack(int until){
     //int temp=times;
     int k;
     for(k=times-1;stack[k]!=until;k--){
             cout<<stack[k]<<" ";
             point[stack[k]].instack=0;
             }
     times=k;
     point[until].instack=0;
     cout<<until;
     cout<<endl;
}
int dfs(int start){
    point[start].dfn=dfnstart;
    point[start].lowint=dfnstart;
    stack[times]=start;
    point[start].instack=1;
    times++;
    dfnstart++;
    for(int i=1;i<=n;i++){
            if(ver[start][i]&&flag[start][i]==0){
                 flag[start][i]=1;
                 if(point[i].dfn==0){
                       father[i]=start;
                       dfs(i);
                       }
                 else if(point[start].dfn>point[i].dfn&&point[i].instack){
                    //   cout<<start<<" "<<point[start].dfn<<endl;
                       point[start].lowint=min(point[start].lowint,point[i].dfn);
                       }
                       }
                       }
    if(point[start].lowint==point[start].dfn){
      //      cout<<start<<" "<<point[start].dfn<<endl;
            outstack(start);
            }
    if(father[start]!=-1){
                point[father[start]].lowint=min(point[father[start]].lowint,point[start].lowint);
                }
    return 0;
}
int main(){
 //   int n,m;
    while(cin>>n>>m){
        dfnstart=1;
        times=0;
        memset(ver,0,sizeof(ver));
        memset(point,0,sizeof(point));
        memset(father,-1,sizeof(father));
        memset(flag,0,sizeof(flag));
        for(int i=0;i<m;i++){
                int temp1,temp2;
                cin>>temp1>>temp2;
                ver[temp1][temp2]=1;
                }
        dfs(1);
        for(int i=1;i<=n;i++){
            //    cout<<i<<" "<<point[i].lowint<<" "<<point[i].dfn<<endl;
                }
        for(int j=1;j<=n;j++){
                if(point[j].dfn==0)
                      dfs(j);
                      }
                      }
        return 0;
        }


 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值