#include using namespace std; /** 思路:其实就是求最小环。每个点的出度都是1,因此构成的图要么是一条链+一个环,要么是几个环, 通过拓扑可以消去链状的部分,对环的部分dfs算最小环即可。 */ const int N = 2e5 + 10; int n; //n个同学 int ans = 0x3f3f3f3f; //答案,初始值为极大值 vector edge[N]; //邻接表 int ind[N]; //入度数组 queue q; //拓扑排序用的队列 bool st[N]; //是不是已经被排除掉 //拓扑排序[这是一个拓扑排序的模板,但里面扩充了本题的st[i]数组标识 ] void topsort() { //入度为0的结点入队列 for (int i = 1; i <= n; i++) if (!ind[i]) q.push(i); //图的广度优先遍历 while (!q.empty()) { int x = q.front(); q.pop(); //标识非环中结点 st[x] = true; //遍历每条出边 for (int i = 0; i < edge[x].size(); i++) { int y = edge[x][i]; ind[y]--; if (!ind[y]) q.push(y);//在删除掉当前结点带来的入度后,是不是入度为0了,如果是将点y入队列 } } } /** 功能:求图中包含点u的环的长度 参数: u 结点 len 长度 */ void dfs(int u, int len) { //标识探测过了 st[u] = true; //遍历所有u开头的边 for (int y:edge[u]) { //如果这个点还没有被探测过,那么继续探索 if (!st[y]) dfs(y, ++len); else { //到这里是发现了环!因为现在在邻接表中只剩下了环,其它的链路都被拓扑排序干掉了, // 结果还是出现访问过的结点,就肯定是发现环了 ans = min(ans, len); //这是递归的出口.此处和其它的递归不一样啊,递归出口居然在代码区, // 而不是在一进递归的位置~ return; } } } int main() { //创建有向图 cin >> n; for (int i = 1; i <= n; i++) { int u; cin >> u; //邻接表方式 edge[i].push_back(u); //入度++ ind[u]++; } //拓扑排序 topsort(); //到这里,st[i]=false的就应该是环中结点,对环的部分dfs算最小环即可 for (int i = 1; i <= n; i++) if (!st[i]) dfs(i, 1); cout << ans << endl; }