Gym - 100712H Bridges (无向图 缩点,Tarjan + 找树的直径)

题目链接

题意:给你一张图,问你 加了一条边之后,最少有多少桥。

如果那些形成一个环的点,点之间的边都不是桥,所以要考虑把这些点缩成一个点。

缩点完成后,就会形成一棵树。找到连接起来最长的边(树的直径),在用一条边把他们从头到尾连起来,那样就去掉了最多的边,剩下的就是我们要求的 桥;

总的来说就是,无向图缩点,然后形成树,找树的直径,再拿边的个数减去树的直径就是答案。

找树的直径,一定要走两次 dfs 找到底层的点,在走一次 dfs 才是数的直径。

#include <bits/stdc++.h>
using namespace std;
#define mem(x,v) memset(x,v,sizeof(x))
const int N = 5e5 + 10;
int n,m,head[N],cnt;
struct node{
    int b,next;
}g[N];
stack<int>q;
vector<int>f[N];
int dfn[N],low[N],times,cont,scc[N],d[N];
bool instk[N];
void add(int x, int y){
    g[++cnt].next = head[x];
    g[cnt].b = y;
    head[x] = cnt;
    return;
}

void tarjan(int u, int fa){
    low[u] = dfn[u] = ++times;
    q.push(u);
    for (int i = head[u]; i; i = g[i].next){
        int v = g[i].b;
        if (v == fa) continue;
        if (!dfn[v]) {
            tarjan(v,u);
            low[u] = min(low[u],low[v]);
        } else low[u]= min(low[u],dfn[v]);
    }
    if (low[u] == dfn[u]){
        cont++;
        while(true){
            int v = q.top(); q.pop();
            scc[v] = cont;
            if (v == u) break;
        }
    }
    return;
}
void dfs(int x,int y){
    d[x] = d[y]+1;
    instk[x] = 1;
    for (int i = 0; i < f[x].size(); i++)
        if (f[x][i] != y && !instk[f[x][i]]) dfs(f[x][i],x);
    return;
}
int main(){
    int T,x,y;
    cin>>T;
    while(T--){
        cnt = 0;
        mem(g,0);
        mem(head,0);
        scanf("%d%d",&n,&m);
        for (int i = 0; i < m; i++){
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
    times = 0;
    cont = 0;
    while(!q.empty()) q.pop();
    for (int i = 0; i <= n; i++){
        low[i] = dfn[i] = scc[i] = 0;
    }
    tarjan(1,0);  //强联通。
    for (int i = 0; i <= cont; i++)
        f[i].clear();
    for (int i = 1; i <= n; i++) //新建边。找树的直径。
        for (int j = head[i]; j; j = g[j].next)
            if (scc[i] != scc[g[j].b]){
                f[scc[g[j].b]].push_back(scc[i]);
                f[scc[i]].push_back(scc[g[j].b]);
            }

    int S = 0,ans = 0;

    mem(instk,0);
    dfs(1,0);
    for (int i = 1; i <= cont; i++) if (d[i] > d[S]) S = i;
    mem(instk,0);
    dfs(S,0);
    for (int i = 1; i <= cont; i++)
        ans = max(ans,d[i]);
    printf("%d\n",cont-ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/kidsummer/article/details/81195125
今日推荐