《洛谷2661-信息传递 》
看到题目后,显然是求一个最小的环。(把每个人看成点,关系看成边。)
但是这里可以对题目做一个转化。
可以发现每个人的只能有一个目标点。
对于环上的点,显然这个环上的任意一个点都不会再往外走。那么只存在别的点连入该环的情况。
别的点连入该环后,别的点显然无法再成环。
那么,我们可以将这些点都删去,最后只留下所有的环。
如何删点?首先可以发现,对于不成环的点,肯定存在一个点入度为0.
那么将这个入度为0的点删去,然后将这个点的目标点的入度也-1。
注意的是,可能我们在删去一个点后,前面的点也变成入度为0了,所以在每次删点后,判断目标点也也是否需要删去。
然后删过的点就不要删了,打上标记。
注意这样处理后,如果去遍历环上的所有点,复杂度还是O(n^2),会T。
但是我们可以发现,对于环上的任意一点,他们的完成时间都是一样的。
那么我们在遍历一个环时,将环上的所有点都打上标记,一个环只遍历一次。
复杂度就降到了O(n)左右。
Code:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<double,int> pii; const int N = 2e5+5; const int M = 4005*4005; #define pi acos(-1) #define INF 1e8 #define INM INT_MIN #define dbg(ax) cout << "now this num is " << ax << endl; inline int read() { int x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } int to[N],in[N],vis[N],ans = INF; void Delete(int x) { vis[x] = 1; in[to[x]]--; if(in[to[x]] == 0 && !vis[to[x]]) Delete(to[x]); } void dfs(int x,int rt,int sum) { vis[x] = 1; if(x == rt && sum != 0) {ans = min(ans,sum);return ;} dfs(to[x],rt,sum+1); } int main() { int n;n = read(); for(int i = 1;i <= n;++i) { to[i] = read(); in[to[i]]++; } for(int i = 1;i <= n;++i) if(in[i] == 0 && !vis[i]) Delete(i); for(int i = 1;i <= n;++i) if(in[i] != 0 && !vis[i]) dfs(i,i,0); printf("%d\n",ans); system("pause"); return 0; }