题意:给出一个无向图,删除两个点让剩余的图的连通分量的数量最大。
思路:考虑每一个点,尝试将其删除,然后计算剩余的连通分量的个数,对于每个连通分量,再求删除某一个点所能增加的最多的连通分量的数量。对于第一步,直接标记一下要删除的点,然后dfs计算一下连通分量的个数即可,对于第二歩,如果再一个一个删肯定要超时了,由于要在一个连通分支中删除一个点,那么这个点要尽可能是割点,可以考虑一下求割点的过程,用tarjan求割点的时候,当lowv>=pre[u]的时候,则说明u是割点,画个图的话应该能理解好一些,没当遇到这种情况,那么删除u的时候,就会增加一个连通分量,这样的话就可以在求割点的时候计算出删除这个割点会增加多少连通分量了,这样的话,答案就能计算出来了,另外还有一些特殊情况需要特判,比如一个连通分支中没有割点的情况。
代码:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=5000+10;
int pre[maxn],vis[maxn],dfs_clock;
int ans,ban,total,step,n,m,temp;
vector<int>G[maxn];
int tarjan(int u,int fa)
{
bool iscut=false;
int child=0,cutnum=0;
int lowu=pre[u]=++dfs_clock;
for(int i=0;i<G[u].size();++i)
{
int v=G[u][i];
if(v==ban) continue;
if(!pre[v])
{
child++;
int lowv=tarjan(v,u);
lowu=min(lowu,lowv);
if(lowv>=pre[u])
{
iscut=true;
cutnum++;
}
}
else if(pre[v]<pre[u]&&v!=fa)
lowu=min(lowu,pre[v]);
}
if(fa<0&&child==1) iscut=false;
if(child==0&&fa==-1) ans=max(ans,total-1);
if(iscut)
ans=max(ans,total+cutnum-(int)(fa==-1));
return lowu;
}
void dfs(int u)
{
temp++;
vis[u]=step;
for(int i=0;i<G[u].size();++i)
{
int v=G[u][i];
if(vis[v]!=step&&v!=ban)
dfs(v);
}
}
void find_conn(int x)
{
step++;
total=0;
ban=x;
bool flag=false;
for(int i=0;i<n;++i)
{
if(vis[i]!=step&&i!=ban)
{
temp=0;
dfs(i);
total++;
if(temp>=2) flag=true;
}
}
if(flag) ans=max(ans,total);
memset(pre,0,sizeof(pre));
dfs_clock=0;
for(int i=0;i<n;++i)
if(!pre[i]&&i!=ban) tarjan(i,-1);
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
while(~scanf("%d%d",&n,&m))
{
int a,b;
for(int i=0;i<n;++i) G[i].clear();
for(int i=0;i<m;++i)
{
scanf("%d%d",&a,&b);
G[a].push_back(b);
G[b].push_back(a);
}
memset(vis,0,sizeof(vis));
ans=0;step=0;
for(int i=0;i<n;++i)
find_conn(i);
printf("%d\n",ans);
}
return 0;
}