bfs和dfs
//求最小环
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,u,y,cnt,mn=0x3fffffff,c,t,p;
int head[200005],col[200005],st[200005];
int bc[200005],b[200005],cn[200005];
//col每个连通块的颜色,st记录每个连通块
//b是否已经记录过,如果是,表示已经构成环了
//cn记录队列的编号,与环的第一个点相减表示该环的个数
struct Data{
int to,nxt;
};
Data edge[400005];
void add(int u,int v)
{
cnt++;
edge[cnt].to=v;//edge[2].nxt
edge[cnt].nxt=head[u];
head[u]=cnt;
}
int dfs(int u,int c)
{
for(int i=head[u];i>0;i=edge[i].nxt)
{
int v=edge[i].to;
if(col[v]==0)
{
col[v]=c;
dfs(v,c);
}
}
}
int dfs2(int u)
{
int v=bc[u];//u的后继
p++;
if(b[v]==1)
return p-cn[v];
cn[v]=p;
b[v]=1;
dfs2(v);
}
int main()
{
scanf("%d",&n);
for(int x=1;x<=n;x++)
{
scanf("%d",&y);
add(x,y);
add(y,x);//设置成无向的
bc[x]=y;
}
// for(int i=1;i<=n;i++)
// {
// for(int j=hd[i];j>0;j=eg[j].nxt)
// cout<<i<<","<<eg[j].to<<endl;
// }
for(int i=1;i<=n;i++)
{
if(col[i]==0)
{
c++;
col[i]=c;
st[++t]=i;//记录一共几个连通块
dfs(i,c);//将能设涉及到的都涉及
}
}
// for(int i=1;i<=t;i++)
// cout<<i<<","<<st[i]<<endl;
for(int i=1;i<=t;i++)
{
memset(cn,0,sizeof cn);
b[st[i]]=1;p=0;
int tmp=dfs2(st[i]);
if(tmp<mn)
mn=tmp;
}
printf("%d\n",mn);
}
/*
9
2 4 2 3 1 7 8 6 6
output:
3 3
*/
并查集
//求最小环
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,u,y,cnt,mn=0x3fffffff,c,t,p;
int fa[200005],st[200005],jud[200005];
int bc[200005],b[200005],cn[200005];
//col每个连通块的颜色,st记录每个连通块
//b是否已经记录过,如果是,表示已经构成环了
//cn记录队列的编号,与环的第一个点相减表示该环的个数
int find(int x)
{
if(x==fa[x]) return x;
find(fa[x]);
}
void uni(int p,int q)
{
fa[q]=p;
}
int dfs2(int u)
{
int v=bc[u];//u的后继
p++;
if(b[v]==1)
return p-cn[v];
cn[v]=p;
b[v]=1;
dfs2(v);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
fa[i]=i;//每个点的父节点是自己
int g,h;
for(int x=1;x<=n;x++)
{
scanf("%d",&y);
bc[x]=y;
g=find(x),h=find(y);
if(g!=h)
uni(g,h);//把两个父亲fa[h]=g进行合并
}
for(int i=1;i<=n;i++)
{
int f=find(i);
if(jud[f]==0) jud[f]=1,st[++t]=f;
}
// for(int i=1;i<=t;i++)
// cout<<i<<","<<st[i]<<endl;
for(int i=1;i<=t;i++)
{
memset(cn,0,sizeof cn);
b[st[i]]=1;p=0;
int tmp=dfs2(st[i]);
if(tmp<mn)
mn=tmp;
}
printf("%d\n",mn);
}
/*
9
2 4 2 3 1 7 8 6 6
*/
并查集-路径压缩
//求最小环
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,u,y,cnt,mn=0x3fffffff,c,t,p;
int fa[200005],st[200005],jud[200005];
int bc[200005],b[200005],cn[200005];
//col每个连通块的颜色,st记录每个连通块
//b是否已经记录过,如果是,表示已经构成环了
//cn记录队列的编号,与环的第一个点相减表示该环的个数
int find(int x)
{
if(x!=fa[x]) fa[x]=find(fa[x]);
else return x;
}
void uni(int p,int q)
{
fa[q]=p;
}
int dfs2(int u)
{
int v=bc[u];//u的后继
p++;
if(b[v]==1)
return p-cn[v];
cn[v]=p;
b[v]=1;
dfs2(v);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
fa[i]=i;//每个点的父节点是自己
int g,h;
for(int x=1;x<=n;x++)
{
scanf("%d",&y);
bc[x]=y;
g=find(x),h=find(y);
if(g!=h)
uni(g,h);//把两个父亲fa[h]=g进行合并
}
// for(int i=1;i<=n;i++)
// cout<<i<<","<<fa[i]<<endl;//并不是所有点的fa[]都更新了,只是在find过程中路径上的点被更新了
for(int i=1;i<=n;i++)
{
int f=find(i);
if(jud[f]==0) jud[f]=1,st[++t]=f;
}
for(int i=1;i<=t;i++)
cout<<i<<","<<st[i]<<endl;
for(int i=1;i<=t;i++)
{
memset(cn,0,sizeof cn);
b[st[i]]=1;p=0;
int tmp=dfs2(st[i]);
if(tmp<mn)
mn=tmp;
}
printf("%d\n",mn);
}
/*
9
2 4 2 3 1 7 8 6 6
*/
tarjan
//求最小环
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,x,y,tp,tim,mn;
int ans=0x3fffffff;
int dfn[200005],low[200005],insk[200005],bc[200005],stk[200005];
//st记录每个连通块
//b是否已经记录过,如果是,表示已经构成环了
//cn记录队列的编号,与环的第一个点相减表示该环的个数
void tarjan(int u)
{
dfn[u]=low[u]=++tim;
insk[u]=1;
stk[++tp]=u;
int v=bc[u];
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(insk[v]==1)
{
low[u]=min(low[u],low[v]);
}
if(low[u]==dfn[u])
{
while(1)
{
mn++;
int now=stk[tp];
tp--;
if(now==u) break;
}
if(mn>1)
ans=min(ans,mn);
}
}
int main()
{
scanf("%d",&n);
for(int x=1;x<=n;x++)
{
scanf("%d",&y);
bc[x]=y;
}
for(int i=1;i<=n;i++)
{
// memset(dfn,0,sizeof dfn);
// memset(low,0,sizeof low);
// tim=0;
mn=0;
if(!dfn[i])
tarjan(i);
}
cout<<ans<<endl;
}