解题思路
知道这个以后比较容易发现要使连通块数量最大要优先处理非环边,因为非环边一删的话连通块个数能+1,环边的话要先删除一条边变成链,再在链上删除一条边才能使连通块个数+1,所以根据贪心思想我们要优先处理非环边,删完非环边后若还有剩余的话再处理环边。假设K够大的话,我们优先删比较大的环,这样的话才能十连通块个数最大。
如何判断环边和非环边??
用DFS暴力,记录每个点的dfs序,若存在一个环,最终一定会回到起点,所以假设当前为u节点时,v是儿子节点,若儿子节点已经标记过了,代表现在从v出发回到了v,若deep【u】-deep【v】>1(非重边),就把这个环存下来。
代码
#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,k,kk,u,v,tot,ans,num,d[4000010],st[4000010],head[4000010];
struct c {
int x,next;
} a[4000010];
void add(int x,int y) {
a[++kk].x=y;
a[kk].next=head[x];
head[x]=kk;
}
void dfs(int x,int fa)
{
d[x]=d[fa]+1;
for(int i=head[x];i;i=a[i].next)
{
int y=a[i].x;
if(y==fa)continue;
if(!d[y])
dfs(y,x);
else if(d[y]<d[x])
{
if(d[x]-d[y]>1)st[++tot]=d[x]-d[y]+1,num+=d[x]-d[y]+1;
}
}
}
int main(){
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
for(int i=1;i<=n;i++)
{
if(!d[i])
ans++,dfs(i,0);
}
if(k<=m-num)
{
printf("%d",k+ans);
return 0;
}
sort(st+1,st+tot+1);
k-=m-num,ans+=m-num;
for(int i=tot;i>0;i--)
{
if(k>=st[i])
k-=st[i],ans+=st[i]-1;
else {
ans+=k-1;
printf("%d\n",ans);
return 0;
}
}
}