给定n个点m条边的无向图, 求删除一个点后的连通块最多有多少个 (求割点)

https://www.acwing.com/problem/content/description/1185/
如何求割点:
x->y:
1.如果从y开始搜能搜到x 那就一定不是割点
2.如果不能搜回去 x不是根节点 删掉x 一定会分成两个不连通的部分
3.如果x是根节点 当至少有两个子节点 才会分成连通块 low[yi]>=dfn[x]

怎么做连通块:
1.统计联通块个数
2.枚举从那个块中删除那个点
枚举删除那个点
求分成哪个部分s 总共s+cnt-1

dfn兼具判重数据的作用 每组测试数据记得判重

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10010,M=30010;
int n,m;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], timestamp;  // 时间戳
int stk[N], top;
int root,ans;//root根节点 ans把每个点删完之后最多几个
void add(int a, int b)  // 添加一条边a->b
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void tarjan(int u){
    dfn[u]=low[u]=++timestamp;
    int cnt=0;//当前快内 已经可以分出来的字数的个数
    for (int i = h[u]; ~i ; i=ne[i] ){
        int j=e[i];
        if(!dfn[j]){//没有遍历过j
            tarjan(j);//下一个点没搜过才搜
            low[u]=min(low[u],low[j]);//用j更新u
            if(low[j]>=dfn[u]) cnt++;//j走不到u上面 所以删了这个u点 连通块会增加1 
            
        }//如果j已经搜过了的话
        else low[u] =min(low[u],dfn[j]);//有搜到过就递归下一个点
    }
    if(u!=root ) cnt++;//u不是根节点 可以单独出来 所以还需要+上父亲的部分
    ans = max(ans,cnt);
}
int main()
{
    while ( cin>>n>>m,n||m ){
        memset(h, -1, sizeof h);
        memset(dfn, 0, sizeof dfn);
        idx=timestamp=0;
        
        while (m -- ){
            int a,b;
            cin >> a>>b;
            add(a, b);
            add(b, a);
        }
        ans=0;//把这个点删完后 最多能分为几块
        int cnt=0;//连通块数量
        for (root=0;root<n;root++){
            if(!dfn[root]){//如果点root没搜索过 连通块数量+1
                cnt++;
                tarjan(root);
            }
        }
        cout << ans+cnt-1<<endl;
    }
    return 0;
}

posted @   liang302  阅读(162)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示
主题色彩