POJ 3352 Road Construction(边双连通分量)

17 篇文章 0 订阅

Description
某个企业想把一个热带天堂岛变成旅游胜地,岛上有n个旅游景点,任意2个旅游景点之间有路径连通(注意不一定是直接连通)。而为了给游客提供更方便的服务,该企业要求道路部门在某些道路增加一些设施。道路部门每次只会选择一条道路施工,在该条道路施工完毕前,其他道路依然可以通行。然而有道路部门正在施工的道路,在施工完毕前是禁止游客通行的。这就导致了在施工期间游客可能无法到达一些景点。为了在施工期间所有旅游景点依然能够正常对游客开放,该企业决定搭建一些临时桥梁,使得不管道路部门选在哪条路进行施工,游客都能够到达所有旅游景点。给出当下允许通行的m条道路,问该企业至少再搭建几条临时桥梁,才能使得游客无视道路部门的存在到达所有旅游景点?
Input
第一行两个整数n和m表示旅游景点数目以及当下允许通行的道路数量,之后m行每行两个整数表示这两个旅游景点有道路可以通行
Output
输出搭建的临时桥梁的最少数目
Sample Input
10 12
1 2
1 3
1 4
2 5
2 6
5 6
3 7
3 8
7 8
4 9
4 10
9 10
Sample Output
2
Solution
题意转化为一个连通的无向图,求至少需要添加几条边,救能保证删除任意一条边,图仍然是连通的。即添加最少的边使得一个无向图任意两个点至少有两条路径可以连通,首先用tarjan算法求边双连通分量,缩点后求每个缩点的度,用ans记录度为2的点,则(ans+1)/2即为答案
Code

#include<iostream>  
#include<cstdio>  
#include<cstring>  
#include<algorithm>  
#include<vector>  
#include<stack>  
#include<cmath>  
#define maxn 5555   
using namespace std;  
vector<int>g[maxn];  
stack<int>st;  
int n,m,scc,index;  
int low[maxn],dfn[maxn],instack[maxn],fa[maxn]; 
void init()//初始化  
{  
    scc=index=0;  
    while(!st.empty())st.pop();  
    for(int i=0;i<maxn;i++)g[i].clear();  
    memset(dfn,0,sizeof(dfn));  
    memset(instack,0,sizeof(instack));  
}  
void tarjan(int u,int f)//求边双连通分量 
{  
    dfn[u]=low[u]=++index;  
    instack[u]=1;  
    st.push(u);  
    int v,size=g[u].size();
    int flag=0;  
    for(int i=0;i<size;i++)  
    {  
        v=g[u][i];  
        if(v==f&&!flag)
        {
            flag=1;
            continue;
        }
        if(!dfn[v])  
        {  
            tarjan(v,u);  
            low[u]=min(low[u],low[v]);  
        }  
        else if(instack[v]) 
            low[u]=min(low[u],dfn[v]);  
    }  
    if(dfn[u]==low[u])  
    {  
        scc++;  
        do  
        {  
            v=st.top();  
            st.pop();  
            fa[v]=scc;  
            instack[v]=0;  
        }while(v!=u);  
    }  
}  
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        for(int i=0;i<m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            g[u].push_back(v);//建边 
            g[v].push_back(u);//建边 
        }
        tarjan(1,1);//求边双连通分量 
        int cnt[maxn];//记录每个缩点的度 
        memset(cnt,0,sizeof(cnt));//初始化 
        for(int i=1;i<=n;i++)//统计每个点的度 
            for(int j=0;j<g[i].size();j++)
            {
                int v=g[i][j];
                if(fa[i]!=fa[v])
                {
                    cnt[fa[i]]++;
                    cnt[fa[v]]++;
                }
            }
        int ans=0;
        for(int i=1;i<=scc;i++)//统计度为2的缩点数 
            if(cnt[i]==2)
                ans++;
        printf("%d\n",(ans+1)/2);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值