【NOIP2015】信息传递

本题在洛谷上的链接:https://www.luogu.org/problemnew/show/P2661


看起来是一道水题,却花费了我大量的宝贵时间。一开始我用了一种比较朴素的做法,枚举传递次数,然后模拟每个人的生日传递到了哪,时间复杂度比较玄,为O(ans*n),只得了80分。然后仔细分析一下这道题,每个人都有一个固定的传递对象,转化到图论上,就是每个点的出度均为1,然后答案要求最少玩几轮可以得知自己的生日,也就是在这样一个图中找到一个长度最小的环。这样的话,做法就不是很固定了。一种思路是,从每个结点出发,逐个遍历并做好标记,如果发现走到一个曾经走过的点,那么就发现了一个环,用他去更新答案,然后跳出(这个环一定是从开始的那个结点出发所能走到的环中最小的),对于每个被标记的点,就不用再从他出发走一遍了,因此复杂度近似O(n)。这种思路并不完善,因为图中有环,也有环上加几条链这种情况。假如我们已经走过了环,那么再走链,一旦遇到环,就会再去更新,显然会出错。怎么保证先走链呢?其实可以换种做法,而且还算是种优化,类似于拓扑排序,我们删掉入度为1的点和从他出发的边,这样就可能又得到一个入度为0的点,继续删除,最终图上就会只剩下环。而且因为每个结点出度均为1,所以每个环都是互不相干的,可以放心大胆的遍历、标记。

 1 #include <cstdio>
 2 #include <algorithm>
 3 
 4 using namespace std;
 5 
 6 inline int get_num() {
 7     int num = 0;
 8     char c = getchar();
 9     while (c < '0' || c > '9') c = getchar();
10     while (c >= '0' && c <= '9')
11         num = num * 10 + c - '0', c = getchar();
12     return num;
13 }
14 
15 const int maxn = 2e5 + 5;
16 
17 int T[maxn], vis[maxn], d[maxn];
18 
19 int main() {
20     int n = get_num(), cnt = 0, ans = 0x3f3f3f3f;
21     for (int i = 1; i <= n; ++i)
22         T[i] = get_num(), ++d[T[i]];
23     for (int i = 1; i <= n; ++i)
24         if (!d[i]) {
25             d[i] = -1;
26             for (int j = T[i]; ; j = T[j]) if (--d[j]) break;
27             else d[j] = -1;
28         }
29     for (int i = 1; i <= n; ++i) {
30         if (vis[i] || d[i] == -1) continue;
31         cnt = 0;
32         for (int j = i; ; j = T[j])
33             if (!vis[j]) vis[j] = ++cnt;
34             else {
35                 ans = min(ans, cnt + 1 - vis[j]);
36                 break;
37             }
38     }
39     printf("%d", ans);
40     return 0;
41 }
AC代码(删除链)

当然,删除链只是一种方法,我们直接搜也不是不可以,但要保证,更新答案是在同一次遍历中,因此可以加一个变量,记录本次遍历最开始的时间戳。

 1 #include <cstdio>
 2 #include <algorithm>
 3 
 4 using namespace std;
 5 
 6 inline int get_num() {
 7     int num = 0;
 8     char c = getchar();
 9     while (c < '0' || c > '9') c = getchar();
10     while (c >= '0' && c <= '9')
11         num = num * 10 + c - '0', c = getchar();
12     return num;
13 }
14 
15 const int maxn = 2e5 + 5;
16 
17 int to[maxn], vis[maxn];;
18 
19 int main() {
20     int n = get_num(), dfn = 0, start, ans = 0x3f3f3f3f;
21     for (int i = 1; i <= n; ++i) to[i] = get_num();
22     for (int i = 1; i <= n; ++i)
23         if (!vis[i]) {
24             start = dfn + 1;
25             for (int p = i; p; p = to[p]) {
26                 ++dfn;
27                 if (vis[p]) {
28                     if (vis[p] >= start)
29                         ans = min(ans, dfn - vis[p]);
30                     break;
31                 } else vis[p] = dfn;
32             }
33         }
34     printf("%d", ans);
35     return 0;
36 }
AC代码(直接遍历)
原文地址:https://www.cnblogs.com/Mr94Kevin/p/9521585.html