给定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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!