《洛谷2661-信息传递 》

看到题目后,显然是求一个最小的环。(把每个人看成点,关系看成边。)

但是这里可以对题目做一个转化。

可以发现每个人的只能有一个目标点。

对于环上的点,显然这个环上的任意一个点都不会再往外走。那么只存在别的点连入该环的情况。

别的点连入该环后,别的点显然无法再成环。

那么,我们可以将这些点都删去,最后只留下所有的环。

如何删点?首先可以发现,对于不成环的点,肯定存在一个点入度为0.

那么将这个入度为0的点删去,然后将这个点的目标点的入度也-1。

注意的是,可能我们在删去一个点后,前面的点也变成入度为0了,所以在每次删点后,判断目标点也也是否需要删去。

然后删过的点就不要删了,打上标记。

注意这样处理后,如果去遍历环上的所有点,复杂度还是O(n^2),会T。

但是我们可以发现,对于环上的任意一点,他们的完成时间都是一样的。

那么我们在遍历一个环时,将环上的所有点都打上标记,一个环只遍历一次。

复杂度就降到了O(n)左右。

Code:

#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;
}
View Code

 

posted @ 2020-07-21 09:40  levill  阅读(118)  评论(0编辑  收藏  举报