题意:给一个无向图,问去掉任意一个点之后最多能有多少个连通分支
思路:求原来图的连通分支数+割点数,在模板上修改一点就好了,转一个挺不错的解释:
在dfs的时候,我们用cut[i]==X表示在dfs树中当i节点被删除时,i节点的X个儿子被切割开来(可以认为cut[i]是i节点与它的儿子连接的桥边的数目)。注意:如果i是根且其儿子只有1个,虽然i不是割点,cut[i]依然=1。如果i节点非割点,那么cut[i]=0。如果i是割点,那么cut[i]就是i被删除后将割出来的儿子数目。
然后我们求出了每个点的cut[i]值,即i点被删除,会有cut[i]个儿子树被割出来。如果i是dfs树的非根节点,那么cut[i]== 切除i之后增加的连通分量数目。如果i是dfs树的根节点,那么cut[i]-1才是切除i之后增加的连通分量数目(想想是不是)。
如果原始cut[i]=0,表示i是孤立的一点,此时cut[i]-1=-1.
如果原始cut[i]=1,表示i为根且有一个儿子,此时cut[i]-1=0.
如果原始cut[i]>=2,表示i为根且分割了>=2个儿子,此时cut[i]-1>=1.
我个人理解为cnt[i]为切掉该点新生成的子树个数,为什么要减一呢,因为切掉之后是原本就有原图的那一个连通块包含在里面了,所以减一。
#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <map>
#include <string>
#include <set>
#include <ctime>
#include <cmath>
#include <cctype>
using namespace std;
#define maxn 100005
#define LL long long
int cas=1,T;
int n,m;
int sum = 0;
int dfs_clock; //时钟,每访问一个结点增1
vector<int>G[maxn]; //图
int pre[maxn]; //pre[i]表示i结点被第一次访问到的时间戳,若pre[i]==0表示还未被访问
int low[maxn]; //low[i]表示i结点及其后代能通过反向边连回的最早的祖先的pre值
bool iscut[maxn]; //标记i结点是不是一个割点
int cut[maxn]; //切割这个结点后把连通块切成多少份
//求出以u为根节点(u在DFS树中的父节点是fa)的树的所有割点和桥
//初始调用dfs(root,-1)
int dfs(int u,int fa)
{
int lowu=pre[u]=++dfs_clock;
int child = 0; //子结点数目
for (int i = 0;i<G[u].size();i++)
{
int v = G[u][i];
if (!pre[v])
{
child++; //未访问过的结点才能算是u的孩子
int lowv = dfs(v,u);
lowu = min(lowu,lowv);
if (lowv >=pre[u])
{
iscut[u]=1; //u是割点
cut[u]++;
// if (lowv > pre[u]) //(u,v)边时桥
// printf("qiao")
}
}
else if (pre[v] <pre[u] && v!=fa) //v!=fa确保了(u,v)是从u到v的反向边
{
lowu = min(lowu,pre[v]);
}
}
if (fa < 0 && child == 1)
iscut[u]=0; //若u是根且孩子数<=1,那么u就不是割点
return low[u]=lowu;
}
void init()
{
dfs_clock = 0;
sum=0;
memset(pre,0,sizeof(pre));
memset(iscut,0,sizeof(iscut));
memset(cut,0,sizeof(cut));
for (int i = 0;i<=n;i++)
G[i].clear();
}
int main()
{
while (scanf("%d%d",&n,&m)!=EOF && n)
{
init();
if (m==0)
{
printf("%d\n",n-1);
continue;
}
for (int i = 0;i<m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
int ans = -100;
for (int i = 0;i<n;i++)
{
if (!pre[i])
{
sum++;
dfs(i,-1);
cut[i]--;
}
ans = max(ans,cut[i]);
}
/* for (int i = 0;i<n;i++)
ans = max(ans,cut[i]);*/
printf("%d\n",ans+sum);
}
//freopen("in","r",stdin);
//scanf("%d",&T);
//printf("time=%.3lf",(double)clock()/CLOCKS_PER_SEC);
return 0;
}