• AcWing 1183 电力


    \(AcWing\) \(1183\) 电力

    题目传送门

    一、题目描述

    给定一个由 \(n\) 个点 \(m\) 条边构成的 无向图 ,请你求出该图 删除一个点 之后,连通块最多有多少

    输入格式
    输入包含多组数据。

    每组数据第一行包含两个整数 \(n,m\)

    接下来 \(m\) 行,每行包含两个整数 \(a,b\),表示 \(a,b\) 两点之间有边连接。

    数据保证无重边。

    点的编号从 \(0\)\(n−1\)

    读入以一行 \(0\) \(0\) 结束。

    输出格式
    每组数据输出一个结果,占一行,表示连通块的最大数量。

    二、核心思路

    • 图可能不是完全连通的,需要 遍历 原图连通块个数\(cnt\)
      注意:这个连通块与点双什么的没有关系,是指 聚合在一起分离 存在的块的意思。我才不告诉你为什么我理解的这么深入~

    • 每个连通块内找下是否存在 割点

      • 如果存在,尝试下删除它后会产生多少个新的连通块数\(S\),就能增加 \(S\) 个连通块
      • 如果不存在,那么没有可以删除的,换句话说,删除哪个节点,最终还是原来那些连通块
      • 在通过割点求连通块时,需要 特判根节点,不是根节点,还需要加上通往根节点那条边指向的连通块,即\(S+1\)
    • 原来有\(cnt\)个,现在你删除一个割点导致增加了\(2\)个独立块的话,其实是\(1->2\),需要把原来的\(1\)再去掉,即\(cnt+S-1\)就是答案

    三、实现代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 100010, M = 300010;
    
    //链式前向星
    int e[M], h[N], idx, w[M], ne[M];
    void add(int a, int b, int c = 0) {
        e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
    }
    int ans; //在处理每个连通块时,尝试删除每一个割点,记录可以生成的孤立块的数量最大值
    
    int dfn[N], low[N], stk[N], timestamp, top, root;
    //点双连通分量 模板
    vector<int> bcc[N];
    int bcnt;
    void tarjan(int u, int fa) {
        int cnt = 0; // 去掉u之后还剩多少个连通块
        low[u] = dfn[u] = ++timestamp;
        stk[++top] = u;
        int son = 0;
    
        for (int i = h[u]; ~i; i = ne[i]) {
            int j = e[i];
            if (j == fa) continue;
            if (!dfn[j]) {
                son++;
                tarjan(j, u);
                low[u] = min(low[u], low[j]);
    
                if (low[j] >= dfn[u]) {
                    cnt++; //把u删除掉,所有的j就都孤立了,多出来1个孤立块
                    int x;
                    bcnt++;
                    do {
                        x = stk[top--];
                        bcc[bcnt].push_back(x);
                    } while (x != j);       //将子树出栈
                    bcc[bcnt].push_back(u); //把割点/树根也丢到点双里
                }
            }
            low[u] = min(low[u], dfn[j]);
        }
        //特判独立点
        if (fa == -1 && son == 0) bcc[++bcnt].push_back(u);
    
        if (u != root) cnt++; // u不是根节点的话,出了自己的子树,还有他的父节点也断开了连接
        ans = max(ans, cnt);  //不断更新生成的孤立块最大数量
    }
    
    void solve(int n, int m) {
        memset(dfn, 0, sizeof dfn);
        memset(h, -1, sizeof h);
        idx = timestamp = 0;
    
        while (m--) {
            int a, b;
            scanf("%d %d", &a, &b);
            a++, b++; //本题点号从0开始,+1后平移到1开始 0≤a,b<n
            add(a, b), add(b, a);
        }
    
        //连通块的最大(多)数量
        ans = 0;
    
        int cnt = 0; //原图中块数量(互相孤立的有多少个块)
        for (root = 1; root <= n; root++)
            if (!dfn[root]) {
                tarjan(root, -1);
                cnt++;     //记录有多少个独立的块
            }
        //按点双缩点后是一棵树,这个是关键
        /*(1) 原来就有cnt个,这个是底
          (2) 尝试枚举每个块,在块里找割点
            ① 有割点,删除此点会产生更多的独立块:
              I.割点是根,连接着S个点,增加S个独立块
              II.割点不是根,连接着S个点,还要加上向上的父节点所属的独立块
            ② 没有割点,随便删除哪个点都没用,不会产生新的独立块
        */
        printf("%d\n", cnt + ans - 1);
    }
    
    int main() {
        int n, m;
        while (scanf("%d %d", &n, &m), n || m)
            solve(n, m);
        return 0;
    }
    
  • 相关阅读:
    Linux学习总结(16)——CentOS 下 Nginx + Tomcat 配置负载均衡
    photo的复数是photos
    APUE1.11:系统调用 库函数
    Linux的man手册共有以下几个章节
    [关于宝宝的一些网上摘抄]
    ZT-Android深入浅出之Binder机 制
    jclass和jobject的迷惑
    ZT JAVA WeakReference
    ZT————pull push mode
    看了xici有写给孩子的信,maybe我也要写给孩子一些东西了
  • 原文地址:https://www.cnblogs.com/littlehb/p/16098762.html
Copyright © 2020-2023  润新知