题意:n个点(编号0--n-1),m条边,问删除哪两个点使得图中连通分量最多,输出这个最多的分量个数。
做法:首先肯定和割点有关,删除一个的话可以删除关联连通分量最多的那个割点,但是删两个就有点无所适从了,因为不能贪心的删最多和次多的,会产生问题。那么,可以考虑枚举第一个点,去找第二个点。找第二个点就和删除一个点使得联通分量最多有点像了,不过需要注意的是,删除了第一个点之后图可以能是不连通的,找第二个点的时候必须要遍历整个图。具体细节看代码。
CODE
#include
using namespace std;
const int N = 5000+10;
struct node{
int v,next;
node(){}
node(int v,int next):
v(v),next(next){}
}E[N*5];
int n,m,top;
int dfs_clock; ///时间戳
int head[N];
int cnt[N]; ///点被删除后可以增加几个联通分量
int dfn[N],low[N]; ///Tarjan用
void Init()
{
dfs_clock = 0;
for(int i = 0;i < N;i++){
cnt[i] = 1; ///删除某一个点必然会一个产生由它父亲而来联通分量
dfn[i] = low[i] = 0;
}
}
void add(int u,int v)
{
E[top] = node(v,head[u]);
head[u] = top++;
}
int Tarjan(int u,int ufa,int fa)
{
dfn[u] = low[u] = ++dfs_clock;
for(int i = head[u];i != -1;i = E[i].next){
int v = E[i].v;
if(v == ufa) continue;
if(v == fa) continue; ///跑到删除的那个点就不dfs了
if(!dfn[v]){
Tarjan(v,u,fa);
low[u] = min(low[u],low[v]);
if(low[v] >= dfn[u]) cnt[u]++; ///是割点
}
else
low[u] = min(low[u],dfn[v]);
}
}
int main(void)
{
while(scanf("%d%d",&n,&m) != EOF){
top = 0;
memset(head,-1,sizeof head);
for(int i = 1;i <= m;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
int ans = 0;
for(int i = 0;i < n;i++){ ///枚举删除的点
Init(); ///每次都要初始化
cnt[i] = 0;
int sum = 0;
for(int j = 0;j < n;j++){ ///图不一定连通,所以要遍历每一个点看是否访问过
if(i != j && !dfn[j]){
sum++;
cnt[j] = 0;
Tarjan(j,-1,i);
}
}
for(int j = 0;j < n;j++){
ans = max(ans,sum+cnt[j]-1); ///sum里面本身包含了这个连通分量,删除这个点产生cnt个分量,本身存在的连通分量应消失
}
}
printf("%d\n",ans);
}
return 0;
}