程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> hdu1269強連通模板

hdu1269強連通模板

編輯:C++入門知識

 

該題也可以用雙向dfs求強連通分量,這裡給出Tarjan算法的模板,更適合變形。

也貼上雙向dfs的代碼吧。

 

代碼Tarjan(這是網上有注釋的代碼,講得很細):

[cpp]
#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <cstdlib>  
using namespace std; 
 
#define MAXN 10010  
#define MAXM 100010  
 
struct Edge 

      int v, next;   
}edge[MAXM];    //邊結點數組   
 
int first[MAXN], stack[MAXN], DFN[MAXN], Low[MAXN], Belong[MAXM]; 
// first[]頭結點數組,stack[]為棧,DFN[]為深搜次序數組,Belong[]為每個結點所對應的強連通分量標號數組   
// Low[u]為u結點或者u的子樹結點所能追溯到的最早棧中結點的次序號   
int instack[10010];  // instack[]為是否在棧中的標記數組   
int n, m, cnt, scnt, top, tot; 
 
void init() 

    cnt = 0; 
    scnt = top = tot = 0;    //初始化連通分量標號,次序計數器,棧頂指針為0   
    memset(first, -1, sizeof(first)); 
    memset(DFN, 0, sizeof(DFN));   //結點搜索的次序編號數組為0,同時可以當是否訪問的數組使用   

 
void read_graph(int u, int v) //構建鄰接表   

     edge[tot].v = v; 
     edge[tot].next = first[u]; 
     first[u] = tot++; 

 
void Tarjan(int v)       //Tarjan算法求有向圖的強連通分量   

     int min, t; 
     DFN[v] = Low[v] = ++tot;    //cnt為時間戳  
     instack[v] = 1;    //標記在棧中   
     stack[top++] = v;      //入棧   
     for(int e = first[v]; e != -1; e = edge[e].next) 
     {   //枚舉v的每一條邊   
           int j = edge[e].v;   //v所鄰接的邊   
           if(!DFN[j]) 
           {   //未被訪問   
               Tarjan(j);    //繼續向下找   
               if(Low[v] > Low[j]) Low[v] = Low[j];  // 更新結點v所能到達的最小次數層   
           } 
           else if(instack[j] && DFN[j] < Low[v]) 
           {   //如果j結點在棧內,   
               Low[v] = DFN[j]; 
           } 
     } 
     if(DFN[v] == Low[v]) 
     {     //如果節點v是強連通分量的根   
           scnt++;   //連通分量標號加1   
           do 
           { 
               t = stack[--top];   //退棧   
               instack[t] = 0;   //標記不在棧中   
               Belong[t] = scnt;   //出棧結點t屬於cnt標號的強連通分量   
           }while(t != v);  //直到將v從棧中退出   
     } 

 
void solve() 

     for(int i = 1; i <= n; i++)   //枚舉每個結點,搜索連通分量  
        if(!DFN[i])  //未被訪問   
           Tarjan(i);  //則找i結點的連通分量   

 
int main() 

    while(scanf("%d%d",&n,&m) && (n || m)) 
    { 
        init(); 
        while(m--) 
        { 
            int u, v; 
            scanf("%d%d", &u, &v); 
            read_graph(u, v); 
        } 
        solve();     //求強連通分量   
        if(scnt == 1) printf("Yes\n");  //只有一個強連通分量,說明此圖各個結點都可達  
        else printf("No\n"); 
    } 
    return 0; 

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;

#define MAXN 10010
#define MAXM 100010

struct Edge
{
      int v, next; 
}edge[MAXM];    //邊結點數組

int first[MAXN], stack[MAXN], DFN[MAXN], Low[MAXN], Belong[MAXM];
// first[]頭結點數組,stack[]為棧,DFN[]為深搜次序數組,Belong[]為每個結點所對應的強連通分量標號數組
// Low[u]為u結點或者u的子樹結點所能追溯到的最早棧中結點的次序號
int instack[10010];  // instack[]為是否在棧中的標記數組
int n, m, cnt, scnt, top, tot;

void init()
{
    cnt = 0;
    scnt = top = tot = 0;    //初始化連通分量標號,次序計數器,棧頂指針為0
    memset(first, -1, sizeof(first));
    memset(DFN, 0, sizeof(DFN));   //結點搜索的次序編號數組為0,同時可以當是否訪問的數組使用
}

void read_graph(int u, int v) //構建鄰接表
{
     edge[tot].v = v;
     edge[tot].next = first[u];
     first[u] = tot++;
}

void Tarjan(int v)       //Tarjan算法求有向圖的強連通分量
{
     int min, t;
     DFN[v] = Low[v] = ++tot;    //cnt為時間戳
     instack[v] = 1;    //標記在棧中
     stack[top++] = v;      //入棧
     for(int e = first[v]; e != -1; e = edge[e].next)
     {   //枚舉v的每一條邊
           int j = edge[e].v;   //v所鄰接的邊
           if(!DFN[j])
           {   //未被訪問
               Tarjan(j);    //繼續向下找
               if(Low[v] > Low[j]) Low[v] = Low[j];  // 更新結點v所能到達的最小次數層
           }
           else if(instack[j] && DFN[j] < Low[v])
           {   //如果j結點在棧內,
               Low[v] = DFN[j];
           }
     }
     if(DFN[v] == Low[v])
     {     //如果節點v是強連通分量的根
           scnt++;   //連通分量標號加1
           do
           {
               t = stack[--top];   //退棧
               instack[t] = 0;   //標記不在棧中
               Belong[t] = scnt;   //出棧結點t屬於cnt標號的強連通分量
           }while(t != v);  //直到將v從棧中退出
     }
}

void solve()
{
     for(int i = 1; i <= n; i++)   //枚舉每個結點,搜索連通分量
        if(!DFN[i])  //未被訪問
           Tarjan(i);  //則找i結點的連通分量
}

int main()
{
    while(scanf("%d%d",&n,&m) && (n || m))
    {
        init();
        while(m--)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            read_graph(u, v);
        }
        solve();     //求強連通分量
        if(scnt == 1) printf("Yes\n");  //只有一個強連通分量,說明此圖各個結點都可達
        else printf("No\n");
    }
    return 0;
}
 

雙向DFS:

[cpp]
#include<iostream>  
#include<cstring>  
using namespace std; 
const int N=10010; 
const int M=100010; 
struct Edge 

    int v,next; 
}edge1[M],edge2[M]; 
 
int vis1[N],vis2[N]; 
int tot1,tot2; 
int first1[N],first2[N]; 
int num[N]; 
int cn,ans; 
int belong[N]; 
int a,b,n,m,j; 
void init() 

    ans=0;tot1=tot2=0;cn=0; 
    memset(first1,-1,sizeof(first1)); 
    memset(first2,-1,sizeof(first2)); 
    memset(vis1,0,sizeof(vis1)); 
    memset(vis2,0,sizeof(vis2)); 

void add_edge(int a,int b) 

    tot1++; 
    edge1[tot1].v=b; 
    edge1[tot1].next=first1[a]; 
    first1[a]=tot1; 
    tot2++; 
    edge2[tot2].v=a; 
    edge2[tot2].next=first2[b]; 
    first2[b]=tot2; 

void DFS_1(int v) 

    vis1[v]=1; 
    for(int i=first1[v];i!=-1;i=edge1[i].next) 
    { 
        if(!vis1[edge1[i].v]) 
        DFS_1(edge1[i].v); 
    } 
    num[cn++]=v; 

void DFS_2(int v) 

    vis2[v]=1; 
    for(int i=first2[v];i!=-1;i=edge2[i].next) 
    { 
        if(!vis2[edge2[i].v]) 
        DFS_2(edge2[i].v); 
    } 

int main() 

    while(cin>>n>>m,n||m) 
    { 
        init(); 
        for(int i=0;i<m;i++) 
        { 
            cin>>a>>b; 
            add_edge(a,b); 
        } 
        for(int i=1;i<=n;i++) 
        { 
            if(!vis1[i]) 
            { 
                DFS_1(i); 
            } 
        } 
        for(int i=cn-1;i>=0;i--) 
        { 
            if(!vis2[num[i]]) 
            { 
                DFS_2(num[i]); 
                ans++; 
            } 
        } 
        if(ans==1) 
        cout<<"Yes"<<endl; 
        else 
        cout<<"No"<<endl; 
 
    } 
    return 0; 

#include<iostream>
#include<cstring>
using namespace std;
const int N=10010;
const int M=100010;
struct Edge
{
    int v,next;
}edge1[M],edge2[M];

int vis1[N],vis2[N];
int tot1,tot2;
int first1[N],first2[N];
int num[N];
int cn,ans;
int belong[N];
int a,b,n,m,j;
void init()
{
    ans=0;tot1=tot2=0;cn=0;
    memset(first1,-1,sizeof(first1));
    memset(first2,-1,sizeof(first2));
    memset(vis1,0,sizeof(vis1));
    memset(vis2,0,sizeof(vis2));
}
void add_edge(int a,int b)
{
    tot1++;
    edge1[tot1].v=b;
    edge1[tot1].next=first1[a];
    first1[a]=tot1;
    tot2++;
    edge2[tot2].v=a;
    edge2[tot2].next=first2[b];
    first2[b]=tot2;
}
void DFS_1(int v)
{
    vis1[v]=1;
    for(int i=first1[v];i!=-1;i=edge1[i].next)
    {
        if(!vis1[edge1[i].v])
        DFS_1(edge1[i].v);
    }
    num[cn++]=v;
}
void DFS_2(int v)
{
    vis2[v]=1;
    for(int i=first2[v];i!=-1;i=edge2[i].next)
    {
        if(!vis2[edge2[i].v])
        DFS_2(edge2[i].v);
    }
}
int main()
{
    while(cin>>n>>m,n||m)
    {
        init();
        for(int i=0;i<m;i++)
        {
            cin>>a>>b;
            add_edge(a,b);
        }
        for(int i=1;i<=n;i++)
        {
            if(!vis1[i])
            {
                DFS_1(i);
            }
        }
        for(int i=cn-1;i>=0;i--)
        {
            if(!vis2[num[i]])
            {
                DFS_2(num[i]);
                ans++;
            }
        }
        if(ans==1)
        cout<<"Yes"<<endl;
        else
        cout<<"No"<<endl;

    }
    return 0;
}

 
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved