D153 拓扑排序 P2661 [NOIP 2015 提高组] 信息传递

D153 拓扑排序 P2661 [NOIP 2015 提高组] 信息传递_哔哩哔哩_bilibili

 

P2661 [NOIP 2015 提高组] 信息传递 - 洛谷

一个有向有环图,求最小环的大小。

思路

先用拓扑排序标记不在环里的点,不在环里的点一定能入队能出队,在环里的点一定不能入队,例如,图中 $5$ 和 $1$

可能存在多个简单环,再用深搜求出每个环的长度,最后取最小值

深搜时,要从环里的点进入,一直找到已访问的点

不可能从环里走出去,因为题目 “每人只会把信息告诉一个人” 限制了没有连向环外的边

注意:$vis$ 数组的双重功能,拓扑找环时,标记哪些点不在环内,DFS 时,标记哪些点已访问

注意:连边的方法,下标向值连边,赋值即完成连边,如 $t[1]=2,t[2]=4,t[4]=3...$

相关板子:

D01【模板】拓扑排序 - 董晓 - 博客园

 

// 拓扑排序 O(n)
#include<bits/stdc++.h>
using namespace std;

const int N=200010;
int n,t[N],rd[N],ans=1e9;
bool vis[N];

void topo(){
  queue<int> q;
  for(int i=1;i<=n;i++) if(!rd[i]) q.push(i);
  while(!q.empty()){
    int u=q.front();q.pop();
    vis[u]=1; //u点能出队,说明其入度为0,标记u点不在环内
    if(--rd[t[u]]==0) q.push(t[u]);
  }
}
void dfs(int x,int s){ //深搜求环长
  if(vis[x]){ //发现x点已访问,那么更新答案
    ans=min(ans,s); 
    return;
  }
  vis[x]=1; //标记x点已访问
  dfs(t[x],s+1);
}
signed main(){
  cin>>n;
  for(int i=1;i<=n;i++){
    cin>>t[i];  //下标i向值ti连边
    rd[t[i]]++; //ti入度+1
  }
  topo();
  for(int i=1;i<=n;i++)if(!vis[i])dfs(i,0); //如果i在环内,则深搜
  cout<<ans;
}

 

// 有向最小环 并查集 
#include<bits/stdc++.h>
using namespace std;

int n,cnt,ans=2e9;
int fa[200005];

int find(int x){
  ++cnt;
  return fa[x]==x?x:find(fa[x]);
}
int main(){
  cin>>n;
  for(int i=1; i<=n; i++) fa[i]=i;
  for(int i=1,t; i<=n; i++){
    cin>>t;
    cnt=0;
    find(t)==i?ans=min(ans,cnt):fa[i]=t;
  }
  cout<<ans;
}

 

// 有向最小环 tarjan O(n)
#include<bits/stdc++.h>
using namespace std;

const int N=200005;
int n;
int dfn[N],low[N],tim,stk[N],top,siz[N],scc[N],cnt;
vector<int> e[N];

void tarjan(int x){
  dfn[x]=low[x]=++tim; stk[++top]=x;
  for(int y:e[x]){
    if(!dfn[y]){
      tarjan(y);
      low[x]=min(low[x],low[y]);
    }
    else if(!scc[y]) low[x]=min(low[x],dfn[y]);
  }
  if(low[x]==dfn[x]){
    ++cnt;
    while(1){
      int y=stk[top--];
      scc[y]=cnt;
      siz[cnt]++;
      if(y==x) break;
    }
  }
}
signed main(){
  cin>>n;
  for(int i=1,x;i<=n;i++){
    cin>>x;
    e[i].push_back(x);
  }
  for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
  
  int ans=2e9;
  for(int i=1;i<=n;i++) 
    if(siz[i]>=2) ans=min(ans,siz[i]);
  cout<<ans;
}

 

posted @ 2026-05-02 18:47  董晓  阅读(62)  评论(0)    收藏  举报