题目:信息传递
思路:
发现一年前写过这道题……
写的什么?
tarjan!
鬼知道发生了什么就过了……
所以我又写了一遍。
其实完全不用tarjan。
先根据传递的关系建图。
每个点都只有一条出边。
n个点,n条边,组成的是一堆环套树结构。
由于要求最小环,所以可以用类似拓扑排序的方法除去不构成环的点。
然后以每个没有走过的点为起点走一遍,标记一个简单环更新最小值就好了。
代码:
一年前的诡异tarjan代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<algorithm>
#include<sstream>
#include<queue>
#include<stack>
#include<set>
using namespace std;
int n;
int To[200005]={0};
int pre[200005]={0},low[200005]={0};
int scc[200005]={0},cnt[200005]={0};
int num=0;
stack<int> s;
int dfs(int x){
pre[x]=low[x]=++num;
s.push(x);
if(!pre[To[x]]){
int y=dfs(To[x]);
low[x]=min(low[x],y);
}else if(!scc[To[x]]){
low[x]=min(low[x],pre[To[x]]);
}
if(low[x]==pre[x]) {
int y;
scc[0]++;
do{
y=s.top();
s.pop();
scc[y]=scc[0];
cnt[scc[0]]++;
}while(y!=x);
}
return low[x];
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&To[i]);
}
for(int i=1;i<=n;i++){
if(!pre[i]) dfs(i);
}
int ans=(1<<30);
for(int i=1;i<=scc[0];i++){
if(cnt[i]!=1&&cnt[i]<ans) ans=cnt[i];
}
printf("%d\n",ans);
return 0;
}
正常的代码:
#include<bits/stdc++.h>
using namespace std;
#define maxn 200000
#define read(x) scanf("%d",&x)
int n;
int to[maxn+5];
int fr[maxn+5];
bool vis[maxn+5];
int main() {
read(n);
for(int i=1;i<=n;i++) {
read(to[i]);
fr[to[i]]++;
}
bool flg=true;
while(flg) {
flg=false;
for(int i=1;i<=n;i++) {
if(fr[i]==0&&vis[i]==0) {
vis[i]=true;
fr[to[i]]--;
flg=true;
}
}
}
int ans=1e9;
for(int i=1;i<=n;i++) {
if(vis[i]) continue;
int x=i,y=0;
while(!vis[x]) {
vis[x]=true;
x=to[x];
y++;
}
ans=min(y,ans);
}
printf("%d",ans);
return 0;
}