本题在洛谷上的链接: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 }
当然,删除链只是一种方法,我们直接搜也不是不可以,但要保证,更新答案是在同一次遍历中,因此可以加一个变量,记录本次遍历最开始的时间戳。
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 }