Last Corollary CodeForces - 1364D(dfs树找最小环)

思路:对于所给的图形来说,可以分为树图和非树图。
两种图的做法不一样,因为树图是没有环的,只有第二种选择。
对于树图来说,我们找出树的每一层有哪几个点,并且保存起来。然后分别查看(0,2,4…)层的总数和(1,3,5…)层的总数,哪一个符合就输出哪一个就行。
对于非树图来讲,就比较麻烦了。
首先我们先找出非树图中最小的环来,假如这个环的长度为len. 如果len<=k的话,那么我们把这个环的点输出就可以了。如果len>k的话,我们可以发现,对于一个长度为len的环,不相邻的点一共有len/2(下取整)个。也就是说,我们找出非树图中最小的环,就一定可以得到符合题意的答案。
那么接下来就是怎么去找非树图的最小环了。具体的我就不赘述了,简而言之就是将一个图先当做树去遍历,记录每一个点出现的次序,如果发现了这个点之前出现的话,就可以直接计算出这个环的长度,然后我们不断的更新最小值,记录一下这个环上面的一个点就行。在dfs的时候记录一下每一个点的先驱节点是什么,方便后面输出。
代码如下:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;

const int maxx=1e5+100;
const int maxm=2e5+100;
struct node{
	int to,next;
}e[maxm<<1];
int head[maxm<<1];
int vis[maxx],dis[maxx],f[maxx];
vector<int> q[maxx];
int n,m,k,tot,ans=inf,pos;

inline void init()
{
	tot=0;
	memset(head,-1,sizeof(head));
	memset(dis,0,sizeof(dis));
	memset(vis,0,sizeof(vis));
}
inline void add(int u,int v)
{
	e[tot].next=head[u],e[tot].to=v,head[u]=tot++;
}
inline int dfs(int u,int fa,int id)
{
	dis[u]=id;
	vis[u]=1;
	f[u]=fa;
	for(int i=head[u];i!=-1;i=e[i].next)
	{
		int to=e[i].to;
		if(to==fa) continue;
		if(vis[to]) //这个点之前遍历过,就说明出现了环
		{
			if(ans>abs(dis[to]-dis[u])+1)
			{
				ans=abs(dis[to]-dis[u])+1;
				pos=u;
			}
		}
		else dfs(to,u,id+1);
	}
}
inline void Dfs(int u,int f,int h)
{
	q[h].push_back(u);
	for(int i=head[u];i!=-1;i=e[i].next)
	{
		int to=e[i].to;
		if(to==f) continue;
		Dfs(to,u,h+1);
	}
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	init();
	int x,y;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	if(m==n-1)
	{
		cout<<1<<endl;
		Dfs(1,0,0);
		int sumj=0,sumo=0;
		int i=0;
		while(q[i].size())
		{
			if(i%2==0) sumj+=q[i].size();
			else sumo+=q[i].size();
			i++;
		}
		int num=(k+1)/2;
		if(sumj>=num)
		{
			for(int i=0;;i+=2)
			{
				for(int j=0;j<q[i].size();j++)
				{
					cout<<q[i][j]<<" ";
					num--;
					if(!num) break;
				}
				if(!num) break;
			}
		}
		else
		{
			for(int i=1;;i+=2)
			{
				for(int j=0;j<q[i].size();j++)
				{
					cout<<q[i][j]<<" ";
					num--;
					if(!num) break;
				}
				if(!num) break;
			}
		}
		return 0;
	}
	vector<int> p;
	dfs(1,0,0);
	if(ans<=k)
	{
		cout<<2<<endl<<ans<<endl;
		for(int i=1;i<=ans;i++) 
			cout<<pos<<" ",pos=f[pos];
		cout<<endl;
	}
	else
	{
		int num=k/2+(k%2==1);
		cout<<1<<endl;
		int cnt=0;
		for(;num;num--)
		{
			cout<<pos<<" ";
			pos=f[pos];
			pos=f[pos];
		}
		cout<<endl;
	}
	return 0;
}

努力加油a啊,(o)/~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

starlet_kiss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值