hdu4612(缩点+树的直径)

题目大意:求一个连通图然后加一条边使得桥的数目最少
题解思路:先把桥两边不是的点所有连通的点都缩成一个点;
然后把缩完的点构成一颗树那么再直径的两端加一条边就是最优方案
注意:判断重边
题目链接

#include<iostream>
#include<cstring>
#include<stack>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int mx =  2e5+10;
#pragma comment(linker,"/STACK:1024000000,1024000000") //扩大栈的容量
stack<int>s;
bool vis[mx];
int dis[mx];
vector<int>G1[mx],G2[mx];
int pre[mx],low[mx],sccno[mx];
int dfn,cnt,bri,n,m;
int last_node,maxs;
void init(){
    memset(pre,0,sizeof(pre));
    memset(sccno,0,sizeof(sccno));
    memset(low,0,sizeof(low));
    dfn = cnt = bri = 0;
    for(int i = 1; i <= n; i++){
        G1[i].clear();
        G2[i].clear();
    }
}
void dfs(int u,int fa){
    pre[u] = low[u] = ++dfn;
    s.push(u);
    bool flag = 0;
    for(int i = 0; i < G1[u].size(); i++){
        int v = G1[u][i];
        if(!pre[v]){
            dfs(v,u);
            low[u] = min(low[v],low[u]);
            if(low[v] > pre[u])
                bri++;
        }
        else if(v != fa || flag)
            low[u] = min(low[v],low[u]);
        if(v == fa) //判读之前是否有重边
            flag = 1;
    }
    if(pre[u] == low[u]){
        ++cnt;
        while(1){
            int v = s.top();
            s.pop();
            sccno[v] = cnt;
            if(v == u)  break;
        }
    }
}
void bfs(int s){
    queue<int>q;
    q.push(s);
    memset(vis,0,sizeof(vis));
    vis[s] = 1;
    dis[s] = 0;
    last_node = s;

    maxs = 0;
    while(!q.empty()){
        int u = q.front();
        q.pop();
        for(int i = 0; i < G2[u].size(); i++){
            int v = G2[u][i];
            if(!vis[v]){
                vis[v] = 1;
                dis[v] = dis[u]+1;
                if(dis[v] > maxs){
                    maxs = dis[v];
                    last_node = v;
                }
                q.push(v);
            }
        }
    }
}
void solve(){
    dfs(1,-1);
    for(int u = 1; u <= n; u++)
        for(int i = 0; i < G1[u].size(); i++){
            int v = G1[u][i];
            if(sccno[u] != sccno[v]){
                G2[sccno[u]].push_back(sccno[v]);
                G2[sccno[v]].push_back(sccno[u]);
            }
        }
    bfs(1);  //先从任意点遍历最长的节点
    bfs(last_node); //然后遍历回去求最长路径就是树的直径
    printf("%d\n",bri-maxs);
}
int main(){
    while(scanf("%d%d",&n,&m),n||m){
        init();
        for(int i = 1; i <= m; i++){
            int u,v;
            scanf("%d%d",&u,&v);
            G1[u].push_back(v);
            G1[v].push_back(u);
        }
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值