hdu4612Warm up(双联通缩点+树直径)

借鉴这里:http://blog.csdn.net/softshell/article/details/9496161

有N个点,M条边(有重边)的无向图,这样图中会可能有桥,问加一条边后,使桥最少,求最少桥的数量。 

缩点后,求出图中的桥的个数,然后重建图必为树,求出树的最长直径,在该直径的两端点连一边,则图中的桥会最少。

双联通缩点有很多不懂滴地方。。。不咋明白。。。问题之一。。vis数组用不用写。。唉。。唉。。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
using namespace std;
#define clr(x) memset(x,0,sizeof(x))
#define maxn 300015
#define maxm 4000015
struct node{
    int to,next;
}e[maxm],e2[maxm];
int head[maxn],tot;
int head2[maxn],tot2;
int col[maxn],dfn[maxn],low[maxn],sta[maxn];
int top,ti,scc;  //scc 双联通分支个数
int n,m;
bool vis[maxn];
void init(){
    memset(head,-1,sizeof(head));
    clr(vis);
    clr(dfn);
    tot=0;
    top=0;
    scc=ti=0;
}
void add(int u,int v){
    e[tot].to=v;
    e[tot].next=head[u];
    head[u]=tot++;
}
void add2(int u,int v){
    e2[tot2].to=v;
    e2[tot2].next=head2[u];
    head2[u]=tot2++;
}
void tarjan(int u,int fa){
    dfn[u]=low[u]=++ti;
    sta[++top]=u;
    vis[u]=true;
    int cnt=0;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(!dfn[v]){
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
        }
        else if(fa==v){
            if (cnt) low[u] = min(low[u],dfn[v]);//重边
            cnt++;
        }
        else if(vis[v])low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u]){
        int x;
        scc++;
        do{
            x = sta[top--];
            vis[x]=false;
            col[x] = scc;
        }while(x!=u);
    }
}
void build(){
    tot2 = 0;
    memset(head2,-1,sizeof(head2));
    for(int i = 1; i <= n; i++){
        for(int j = head[i]; j!=-1; j = e[j].next){
            int v = e[j].to;
            if(col[i]!=col[v])
                add2(col[i],col[v]);
        }
    }
}
int r;//直径
int bfs(int x){
    queue<int>q;
    int dis[maxn];
    int end;
    memset(dis,-1,sizeof(dis));
    q.push(x);
    dis[x]=0;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        r=dis[u];
        end=u;
        for(int i=head2[u];i!=-1;i=e2[i].next){
            int v=e2[i].to;
            if(dis[v]==-1){
                dis[v]=dis[u]+1;
                q.push(v);
            }
        }
    }
    return end;
}
int main(){
    int u,v;
    while(scanf("%d%d",&n,&m)!=EOF){
        init();
        if(n==0&&m==0)break;
        for(int i=0;i<m;i++){
            scanf("%d%d",&u,&v);
            if(v==u)continue;
            add(u,v);
            add(v,u);
        }
        tarjan(1,-1);
        build();
        r=0;
        bfs(bfs(1));
        printf("%d\n",scc-r-1);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值