强连通相关: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;
    }
}



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值