poj3177重修道路——边双连通分量缩点

题目:http://poj.org/problem?id=3177

找桥,缩点,总之都是板子;

对于每个叶子,互相连一条边即可;若最后剩下一个,则去和根节点连边;

所以叶子节点数+1再/2即答案。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,ct=1,head[5005],reg[5005],c[5005],dc,dfn[5005],low[5005],tim,ans;
bool bri[10005];
struct N{
    int to,next;
    N(int t=0,int n=0):to(t),next(n) {}
}edge[10005],dcc[10005];
void tarjan(int x,int e)
{
    tim++;
    dfn[x]=tim;
    low[x]=tim;
    for(int i=head[x];i;i=edge[i].next)
    {
        int u=edge[i].to;
//        if(i==(e^1))continue;
        if(!dfn[u])
        {
            tarjan(u,i);
            low[x]=min(low[x],low[u]);
            if(low[u]>dfn[x])bri[i]=1,bri[i^1]=1;//!!!
        }
        else if(i!=(e^1))
            low[x]=min(low[x],dfn[u]);
    }
}
void dfs(int x)
{
    c[x]=dc;
    for(int i=head[x];i;i=edge[i].next)
    {
        int u=edge[i].to;
        if(!c[u]&&!bri[i])
            dfs(u);
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)==2)
    {
        memset(head,0,sizeof head);
        memset(reg,0,sizeof reg);
        memset(c,0,sizeof c);
        memset(dfn,0,sizeof dfn);
        memset(low,0,sizeof low);
        memset(bri,0,sizeof bri);
        ct=1;ans=0;dc=0;tim=0;
        for(int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            edge[++ct]=N(y,head[x]);head[x]=ct;
            edge[++ct]=N(x,head[y]);head[y]=ct;
        }
        tarjan(1,0);
        for(int i=1;i<=n;i++)
        {
            if(c[i])continue;
            dc++;
            dfs(i);
        }
        for(int i=2;i<=ct;i+=2)
        {
            int u=edge[i].to,v=edge[i^1].to;
            if(c[u]==c[v])continue;
            reg[c[u]]++;reg[c[v]]++;
        }
        for(int i=1;i<=dc;i++)
            if(reg[i]==1)ans++;
        printf("%d\n",(ans+1)/2);
    }
    return 0;
}

 

posted @ 2018-03-29 00:19  Zinn  阅读(126)  评论(0编辑  收藏  举报