強連通相關:poj1236,poj2186,poj2762,hdu4738

相關的概念:

割邊:在連通圖中,刪除了連通圖的某條邊後,圖不再連通。這樣的邊被稱爲割邊,也叫做橋。


割點:在連通圖中,刪除了連通圖的某個點以及與這個點相連的邊後,圖不再連通。這樣的點被稱爲割點。


對於有向圖上的2個點a,b,若存在一條從a到b的路徑,也存在一條從b到a的路徑,那麼稱a,b是強連通的。


對於有向圖上的一個子圖,若子圖內任意點對(a,b)都滿足強連通,則稱該子圖爲強連通子圖。


非強連通圖有向圖的極大強連通子圖,稱爲強連通分量。


對於一個無向圖的子圖,當刪除其中任意一條邊後,不改變圖內點的連通性,這樣的子圖叫做邊的雙連通子圖。而當子圖的邊數達到最大時,叫做邊的雙連通分量。


將有向圖的所有的有向邊替換爲無向邊,所得到的圖稱爲原圖的基圖。如果一個有向圖的基圖是連通圖,則有向圖是弱連通圖。 


tarjan基本模板

#include<bits/stdc++.h>
using namespace std;
#define N 30000
int dfn[N];//深搜的次序
int low[N];//能追溯到的最早的次序
int head[N];//鄰接表
int belong[N];//屬於哪個強連通分量
int f[N];//父節點
bool instack[N];
int k,cnt,num;//k是鄰接表中邊的數量,cnt強連通分量個數,num深搜的次序。
struct edge
{
    int u,v,next;
} e[N*5];

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

stack<int>s;

void dfs(int u,int id)//有向圖不需要參數id
{
    dfn[u]=low[u]=++num;
    instack[u]=true;
    s.push(u);
    for(int i=head[u]; i!=-1; i=e[i].next)
    {
        if(i==(1^id)) continue;//無向圖需要這個條件
        int v=e[i].v;
        if(!dfn[v])
        {
            f[v]=u;
            dfs(v,i);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        cnt++;
        int v;
        do
        {
            v=s.top();
            s.pop();
            instack[v]=false;
            belong[v]=cnt;
        }
        while(u!=v);
    }
}

int main()
{
    int n,m,x,y;
    cin>>n>>m;
    memset(head,-1,sizeof(head));
    while(m--)
    {
        cin>>x>>y;
        add(x,y);
        add(y,x);
    }
    for(int i=1; i<=n; i++)
    {
        if(!dfn[i])
        {
            dfs(i);//有向圖
            dfs(i,-1);//無向圖
        }
    }
}


相關題目poj1236 

題意:給出點的數量,給出與之相連的單向邊。至少選幾個點,能從這些點到達所有點。至少加多少條邊,能從任何一個點到達所有點。

思路:求強連通分量,縮點,求出新圖的入度爲0的點個數n,出度爲0的點個數m。

問題1:ans=n; 問題2:ans=max(n,m);

#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
#define N 155
int n,k,num,cnt;
int head[N],dfn[N],in[N],out[N],belong[N],low[N];
bool instack[N];
struct edge
{
    int u,v,next;
} e[N*N];
void  add(int u,int v)
{
    e[k].u=u;
    e[k].v=v;
    e[k].next=head[u];
    head[u]=k++;
}
stack<int>s;
void dfs(int u)
{
    dfn[u]=low[u]=++num;
    instack[u]=true;
    s.push(u);
    for(int i=head[u]; i!=-1; i=e[i].next)
    {
        int v=e[i].v;
        if(!dfn[v])
        {
            dfs(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        cnt++;
        int v;
        do
        {
            v=s.top();
            s.pop();
            instack[v]=false;
            belong[v]=cnt;
        }
        while(u!=v);
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    cin>>n;
    for(int i=1; i<=n; i++)
    {
        int x;
        while(cin>>x,x)
        {
            add(i,x);
        }
    }
    for(int i=1; i<=n; i++)
    {
        if(!dfn[i])
            dfs(i);
    }
    for(int i=0; i<k; i++)//求出縮點後的新圖中的入度和出度
    {
        int x=belong[e[i].u],y=belong[e[i].v];
        if(x!=y)
            in[y]++,out[x]++;
    }
    int x=0,y=0;
    for(int i=1; i<=cnt; i++)
    {
        if(!in[i]) x++;
        if(!out[i]) y++;
    }
    if(cnt==1)//特例,強連通分量只有一個,不需要加邊
    {
        cout<<1<<endl;
        cout<<0<<endl;
    }
    else
    {
        cout<<x<<endl;
        cout<<max(x,y)<<endl;
    }
}

poj2186

題意:有n只牛,牛A認爲牛B很牛,牛B認爲牛C很牛。給你M個關係(誰認爲誰牛),如果牛A認爲牛B很牛,牛B認爲牛C很牛。那麼我們就認爲牛A認爲牛C很牛。求大家都認爲它很牛的牛有幾隻。

思路:求強連通分量,縮點,出度爲0的點只能有一個,答案就是該連通分量中的點的數量。

#include<cstring>
#include<iostream>
#include<stack>
using namespace std;
#define N 30000
int dfn[N],low[N],head[N],belong[N],out[N];
bool instack[N];
int k,cnt,num;
struct edge
{
    int u,v,next;
} e[N*5];
void add(int u,int v)
{
    e[k].u=u;
    e[k].v=v;
    e[k].next=head[u];
    head[u]=k++;
}
stack<int>s;
void dfs(int u)
{
    dfn[u]=low[u]=++num;
    instack[u]=true;
    s.push(u);
    for(int i=head[u]; i!=-1; i=e[i].next)
    {
        //if(i==(1^id)) continue;
        int v=e[i].v;
        if(!dfn[v])
        {
            dfs(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        cnt++;
        int v;
        do
        {
            v=s.top();
            s.pop();
            instack[v]=false;
            belong[v]=cnt;
        }
        while(u!=v);
    }
}
int main()
{
    int n,m,x,y;
    cin>>n>>m;
    memset(head,-1,sizeof(head));
    while(m--)
    {
        cin>>x>>y;
        add(x,y);
    }
    for(int i=1; i<=n; i++)
    {
        if(!dfn[i])
        {
            dfs(i);
        }
    }
    int c=0;
    for(int i=0; i<k; i++)
    {
        if(belong[e[i].u]!=belong[e[i].v])
            out[belong[e[i].u]]++;
    }
    for(int i=1; i<=cnt; i++)
        if(!out[i]) c++;
    if(c!=1)
        cout<<0<<endl;
    else
    {
        int ans=0;
        for(int i=1; i<=n; i++)
            if(out[belong[i]]==0)
                ans++;
        cout<<ans<<endl;
    }
}

poj2762

題意:給出一個有向圖,求出該圖是否能滿足給出兩個點,能從一個a到b或者從b到a。

思路:一個強連通分量中的點肯定滿足,所以先縮點,在該弱連通圖中判斷改圖是否是單鏈的(toposort),若有分叉則分叉上的兩個點是不能互達的。

#include<cstring>
#include<iostream>
#include<stack>
#include<queue>
using namespace std;
#define N 3000
int dfn[N],low[N],head[N],belong[N],in[N],out[N];
bool instack[N];
int k,cnt,num;
struct edge
{
    int u,v,next;
} e[N*5];
void add(int u,int v)
{
    e[k].u=u;
    e[k].v=v;
    e[k].next=head[u];
    head[u]=k++;
}
stack<int>s;
void dfs(int u)
{
    dfn[u]=low[u]=++num;
    instack[u]=true;
    s.push(u);
    for(int i=head[u]; i!=-1; i=e[i].next)
    {
        //if(i==(1^id)) continue;
        int v=e[i].v;
        if(!dfn[v])
        {
            dfs(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        cnt++;
        int v;
        do
        {
            v=s.top();
            s.pop();
            instack[v]=false;
            belong[v]=cnt;
        }
        while(u!=v);
    }
}
vector<int>E[N*5];//新圖的鄰接表
queue<int>q;
bool toposort()
{
    while(!q.empty()) q.pop();
    for(int i=1; i<=cnt; i++)
        if(!in[i]) q.push(i);
    if(q.size()>1) return false;//入點有多個不滿足
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=0; i<E[u].size(); i++)
        {
            int v=E[u][i];
            if(!--in[v])
                q.push(v);
        }
        if(q.size()>1) return false;//有分叉不滿足
    }
    return true;
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n,m,x,y;
        cin>>n>>m;
        memset(head,-1,sizeof(head));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        num=cnt=k=0;
        while(m--)
        {
            cin>>x>>y;
            add(x,y);
        }
        for(int i=1; i<=n; i++)
        {
            if(!dfn[i])
            {
                dfs(i);
            }
        }
        memset(in,0,sizeof(in));
        for(int i=1; i<=cnt; i++)
            E[i].clear();
        for(int i=0; i<k; i++)
        {
            int x=belong[e[i].u];
            int y=belong[e[i].v];
            if(x!=y)
            {
                in[y]++;
                E[x].push_back(y);
            }
        }
        if(toposort())
            cout<<"Yes"<<endl;
        else
            cout<<"No"<<endl;
    }
}

hdu4738

題意: 曹操有N個島,這些島用M座橋連接起來,每座橋有士兵把守(也可能沒有),周瑜想讓這N個島不連通,但只能炸掉一座橋,並且炸掉一座橋需要派出不小於守橋士兵數的人。

思路:首先判斷圖是否連通,不連通則不需要去炸橋,輸出0,圖連通,則可以用Tarjan找割邊,割邊不存在輸出-1表示不能達到目的,找到所有的割邊,只需要炸掉其中守兵數最少的橋即可。

PS: 橋的守兵數爲0時,也需要派出一個人去炸橋!

#include<bits/stdc++.h>
using namespace std;
#define N 2000
#define inf 0x7ffffff
int dfn[N],low[N],head[N],belong[N],f[N];
bool instack[N];
int k,cnt,num;
struct edge
{
    int u,v,w,next;
} e[N*N];
void add(int u,int v,int w)
{
    e[k].u=u;
    e[k].v=v;
    e[k].w=w;
    e[k].next=head[u];
    head[u]=k++;
}
stack<int>s;
void dfs(int u,int id)
{
    dfn[u]=low[u]=++num;
    instack[u]=true;
    s.push(u);
    for(int i=head[u]; i!=-1; i=e[i].next)
    {
        if(i==(1^id)) continue;
        int v=e[i].v;
        if(!dfn[v])
        {
            f[v]=u;
            dfs(v,i);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        cnt++;
        int v;
        do
        {
            v=s.top();
            s.pop();
            instack[v]=false;
            belong[v]=cnt;
        }
        while(u!=v);
    }
}
int main()
{
    int n,m,x,y,w;
    while(cin>>n>>m,n+m)
    {
        memset(head,-1,sizeof(head));
        memset(instack,false,sizeof(instack));
        memset(f,-1,sizeof(f));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        k=cnt=num=0;
        while(m--)
        {
            cin>>x>>y>>w;
            add(x,y,w);
            add(y,x,w);
        }
        int c=0;
        for(int i=1; i<=n; i++)
        {
            if(!dfn[i])
            {
                c++;
                dfs(i,-1);
            }
        }
        int ans=inf;
        for(int i=0; i<k; i+=2)
        {
            int u=e[i].u;
            int v=e[i].v;
            int w=e[i].w;
            if(belong[u]!=belong[v])//滿足此條件的是割邊
                ans=min(ans,w);
        }
        if(c>1) ans=0;//不連通
        else if(ans==0) ans=1;//沒有人把守也要派一個人去才能炸掉橋
        else if(ans>=inf) ans=-1;//不存在割邊
        cout<<ans<<endl;
    }
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章