题意
给出 n n 个点,条边的无向图,求出删除一个点后,最多图中能形成多少个联通分量。
题解
删除点能够增加原图连通分量的是割点,所以本题就是求删除一个割点后,联通分量最大的个数。
首先使用Tarjan算法要把所有的割点找出来,找割点的时候同时记录他的孩子有多少个。但是孩子的个数并不是删除他后能够新增的连通分量个数,原因有如下几点:
- 若他是遍历时候的根节点,如果他有一个孩子,那么删除之后并不能增加连通分量的个数;
- 由于原图可能有多个连通分量,所以孩子数目要-1.
需要注意的是,当dfs树中的孩子不存在返祖边的时候,才能算是一个去除一个点后的连通分支个数。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
const int nmax = 1e6+1000;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const ull p = 67;
const ull MOD = 1610612741;
int N,M;
struct edge{
int nxt,to;
};
struct Cut_Tarjan{
int dfn[nmax],low[nmax],cutnum[nmax],head[nmax],dfs_clock,n,scc,tot;
bool iscut[nmax];
edge e[nmax<<1];
void add_edge(int u, int v){
e[tot].to = v, e[tot].nxt = head[u];
head[u] = tot++;
}
void init(int n){
memset(head,-1,sizeof head);
memset(dfn,0,sizeof dfn);
memset(cutnum,0,sizeof cutnum);
memset(iscut,0,sizeof iscut);
dfs_clock = tot = scc = 0;
this->n = n;
}
void dfs(int u,int fa){
int child = 0;
low[u] = dfn[u] = ++dfs_clock;
for(int i = head[u];i!=-1;i = e[i].nxt){
int v = e[i].to;
if(v != fa){
if(!dfn[v]){
child ++;
dfs(v,u);
low[u] = min(low[u],low[v]);
if(fa != -1 && low[v] >= dfn[u]) cutnum[u] ++,iscut[u] = true;
}else if(dfn[v]<low[u]) low[u] = min(low[u],dfn[v]);
}
}
if(fa == -1 && child > 1) iscut[u] = true;
if(fa == -1) cutnum[u] = child - 1;
}
void start(){
for(int i = 1;i<=n;++i) if(!dfn[i]) dfs(i,-1),scc++;
}
}cut_t;
int main(){
while(scanf("%d %d",&N,&M) != EOF){
if(N == 0 && M == 0) break;
cut_t.init(N);
int u,v;
for(int i = 1;i<=M;++i){
scanf("%d %d",&u,&v);
cut_t.add_edge(u+1,v+1);
cut_t.add_edge(v+1,u+1);
}
cut_t.start();
int ans = 0;
for(int i = 1;i<=N;++i) ans = max(ans,cut_t.scc + cut_t.cutnum[i]);
printf("%d\n",ans);
}
return 0;
}