题目描述
有 nn 个同学(编号为 11 到 nn )正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为 ii 的同学的信息传递对象是编号为 T_iTi 的同学。
游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息, 但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自 己的生日时,游戏结束。请问该游戏一共可以进行几轮?
输入格式
共2行。
第1行包含1个正整数 nn ,表示 nn 个人。
第2行包含 nn 个用空格隔开的正整数 T_1,T_2,cdotscdots,T_nT1,T2,⋯⋯,Tn ,其中第 ii 个整数 T_iTi 表示编号为 ii 的同学的信息传递对象是编号为 T_iTi 的同学, T_i leq nTi≤n 且 T_i eq iTi≠i 。
题意:求一个有向图的最小环
输入:
5 2 4 2 3 1
输出:
3
思路:由于每一个人只有一个操作对象,那么这个有向图每一个连通分支必定有一个环,
那么我的做法就是对于在所有标记为0的点开始DFS,对其中经过的点打上某一次DFS的标记,
在对某些遍历过的连通分支的标记为0的点进行操作时,直接跳出。
每次找到环之后计算出所用的轮数,直到所有的点的标记都不为0。预计时间复杂度为 O(n)
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAXN=2e5+10; int vis[MAXN]; int nx[MAXN],lp; int cur[MAXN]; int ans=0x3f3f3f3f; void dfs(int st) { lp++; int cnt=0,t=st; while(vis[t]==0) { cur[t]=cnt; cnt++; vis[t]=lp; //做上趟数标记 t=nx[t]; if(vis[t]!=lp&&vis[t]!=0) //发现是之前操作过的连通分支,则跳出 return ; } ans=min(ans,cnt-cur[t]); } int main() { int n; cin>>n; for(int i=1;i<=n;++i) scanf("%d",&nx[i]); for(int i=1;i<=n;++i) { if(!vis[i]) dfs(i); } cout<<ans; return 0; }
判断是否属于同一连通分支也可以用并查集,但是这里地方太小,我写不下(滑稽)
等改天有时间了(?),就来写一下并查集的做法。(也许不咕)
学业繁忙,告辞