0902-图论+模拟(水题)-洛谷P2661 信息传递

版权声明:虽然我只是个小蒟蒻但转载也请注明出处哦 https://blog.csdn.net/weixin_42557561/article/details/82313982

传送门

大致题意

给出 n 个点,每个点只有一条出边,可以有多条入边,求图中最小环

分析

本以为是并查集判环,结果看到题的第一眼就觉得可以用tarjan,最后选择了深搜【笑哭】

思路:

从入度为0 的点开始往下深搜,记录一个时间戳,如果节点 u 的dfn 大于它到达的节点 v 的dfn,就说明 v 先于 u 出现,而又被 u 搜到,则肯定构成一个环,而这个环的大小就是 dfn [ u ] - dfn [ v ] + 1

按照这样的思路只能得60分,为什么呢?

因为这是假算法,举一个反例,大家自己领悟:

(能过60分都是看脸啊!!)

然后我思考了一番,每次重新dfs的时候我就清空之前的时间戳,嗯,这下总该正确了吧

是的,正确了,但是T了两个点(80分)

我又双叒叕思考,发现这样搞的话,会重复计算很多次,好吧好吧,再来!

然后可爱的gsj同学,帮我解决了难题,我们用vis数组标记i这个节点是在第几轮dfs里被遍历到的,有点并查集的感觉,如果u的终点 v 不是和 u 在一轮里被遍历到的并且也不为0,就可以直接return了

这下是真的好啦!!!!(100分)

代码

#include<cstdio>
#include<cmath>
#include<cstring> 
#include<iostream> 
#include<algorithm>
#define N 200009
#define in read()
using namespace std;
int dfn=0,to[N],n;
inline int read(){
	char ch;int res=0;
	while((ch=getchar())<'0'||ch>'9');
	while(ch>='0'&&ch<='9'){
		res=(res<<3)+(res<<1)+ch-'0';
		ch=getchar();
	}
	return res;
}
int ans=N,dfs[N],vis[N];
void getdfn(int u,int id){
	dfs[u]=++dfn;
	vis[u]=id;
	int v=to[u];
	if(vis[v]!=id&&vis[v])  return;
	if(!dfs[v]) getdfn(v,id);
	if(dfs[v]<dfs[u]) 	ans=min(ans,dfs[u]-dfs[v]+1);
}
int du[N];
int main()
{
	n=in;
	int i;
	for(i=1;i<=n;++i) to[i]=in,du[to[i]]++;
	for(i=1;i<=n;++i)
		if(!du[i]){
			memset(dfs,0,sizeof(dfs));
			dfn=0,getdfn(i,i);
		} 
	printf("%d",ans);
	return 0;
}

所以总结一下判环的方法:

  1. 并查集
  2. tarjan
  3. 深搜+标记
  4. 拓扑排序(之前有次训练的题就用到了)

猜你喜欢

转载自blog.csdn.net/weixin_42557561/article/details/82313982
今日推荐