版权声明:虽然我只是个小蒟蒻但转载也请注明出处哦 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;
}
所以总结一下判环的方法:
- 并查集
- tarjan
- 深搜+标记
- 拓扑排序(之前有次训练的题就用到了)