牛客CSP集训营2-B.沙漠点列(图论)

链接:https://ac.nowcoder.com/acm/contest/1101/B
来源:牛客网

我们称一张无向图是仙人掌,当且仅当这张无向图连通且每条边最多属于一个简单环。我们称一张无向图是沙漠,当且仅当这张无向图中所有连通子图都是仙人掌。

给出一个 n个点,m条边的沙漠,你可以删去其中的k 条边。求能分成的连通块数量最大值。

【题解】:题目描述的一段话其实表达的意思是给出的图都是简单环,知道这个以后比较容易发现要使连通块数量最大要优先处理非环边,因为非环边一删的话连通块个数能+1,环边的话要先删除一条边变成链,再在链上删除一条边才能使连通块个数+1,所以根据贪心思想我们要优先处理非环边,删完非环边后若还有剩余的话再处理环边。假设K够大的话,我们优先删比较大的环,这样的话才能十连通块个数最大化。但难点就在于判断环边和非环边。我们采用dfs标记法,通过deep数组表示深度(时间戳),假设当前为u节点时,v是儿子节点,若儿子节点已经标记过了,且deep【u】-deep【v】>1(注意环的大小要大于2,不然就是一条重边)

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+10;
int n,m,k,ans=0,tot=0,cnt=0,head[maxn],deep[maxn]; 
vector<int>s;
struct node{
	int y,next;
}a[maxn<<1];
void add(int x,int y)
{
	a[cnt].y=y;a[cnt].next=head[x];head[x]=cnt++;
}
void dfs(int u,int fa)
{
	deep[u]=deep[fa]+1;
	for(int i=head[u];i!=-1;i=a[i].next)
	{
		int v=a[i].y;
		if(v==fa) continue;
		if(!deep[v]) dfs(v,u);
		else if(deep[v]<deep[u])
		{
			if(deep[u]-deep[v]>1) s.push_back(deep[u]-deep[v]+1),tot+=deep[u]-deep[v]+1;//tot为总的环边个数
		}
	}
}
int main()
{
	int u,v;
	memset(deep,0,sizeof(deep));
	memset(head,-1,sizeof(head));
	scanf("%d %d %d",&n,&m,&k);
	for(int i=0;i<m;++i) scanf("%d %d",&u,&v),add(u,v),add(v,u);
	for(int i=1;i<=n;++i) if(!deep[i]) ans++,dfs(i,0);
	if(k-(m-tot)<=0) {
		printf("%d\n",k+ans);return 0;
	}
	sort(s.begin(),s.end());
	k-=(m-tot);ans+=m-tot;
	for(int i=s.size()-1;i>=0&&k>0;--i)
	{
		if(k>=s[i]) k-=s[i],ans+=s[i]-1;
		else{
			ans+=k-1;break;
		}
	}
	printf("%d\n",ans);
}
发布了39 篇原创文章 · 获赞 0 · 访问量 1106

猜你喜欢

转载自blog.csdn.net/qq_42479630/article/details/102988432
今日推荐