【图论】洛谷P2661 信息传递(拓扑思想求有向图最小环)

题目

LP2661

思路

题目看似很复杂,但实际就是要求这个有向图的最小环。
dfs效率太低,这里考虑拓扑思想求最小环。


先复习一下拓扑排序:
将有向图输出成一个序列,使得对于每个有向边u->v,在这个序列中u在v的前面。
求法:(1)选择一个没有直接前驱的顶点,并输出;
(2)从图中删除该顶点,同时删去所有它发出的有向边;
(3)依次循环以上两步,直到:全部顶点已经输出(排序结束),或被迫跳出循环(图中存在环)


如果一个人的入度为0,则肯定不可能成环,那么把这个人和他连出的边删去(即标记这个人并将他下一个人的入度减 1),如果下一个人的入度为0则将他也删去……最后把所有入度为0的人都删去了,剩下的都是环。
本题独有的条件是,每个人的出度为1,所以每个人只能在一个环中,所以每个环是分开的,直接一遍dfs就可求得最小环。

代码

#include 
#include 
#include 
#include 
#include 
#define _for(i,a,b) for(int i = a; i
#define _rep(i,a,b) for(int i = a; i<=b; i++)
using namespace std;

const int maxn = 200000+10;
int n, G[maxn], ru[maxn], ans;
bool del[maxn], vis[maxn];

void remove(int u){
	del[u] = true;
	ru[G[u]]--;
	if (!del[G[u]] && !ru[G[u]]) remove(G[u]);
}

void dfs(int u, int r, int len){
	vis[u] = true;
	if (G[u] == r){
		ans = min(ans, len);
		return;
	}
	if (!del[G[u]] && !vis[G[u]])
		dfs(G[u], r, len+1);
}

int main(){
	scanf("%d",&n);
	_rep(i,1,n){
		scanf("%d",&G[i]);
		ru[G[i]]++;
	}
	
	_rep(i,1,n) if (!del[i] && !ru[i]) remove(i);
	ans = n;
	_rep(i,1,n)
		if (!del[i] && !vis[i])
			dfs(i,i,1);

	printf("%d\n", ans);
	return 0;
}

你可能感兴趣的:(9.noip及时复习)