洛谷P2661-信息传递(拓扑排序求最小环)

题意:洛谷P2661

分析:

由题可知,一个人能经别人知道自己的生日,则他必然在一个环中,所以显然题目是让我们求图中最小的环。结合本题的特性,每个人的出度都为 1 1 1,那么每个人必定只在一个环中。如果有人的入度为 0 0 0则说明他一定不在环中,我们可以用拓扑的思想把他及他所连的边删去,然后继续做拓扑排序删除入度为 0 0 0的点即可。删完点之后剩下的就全是环了。这个时候我们对每个环做一次 d f s dfs dfs即可。 d f s dfs dfs的过程中将一个环内所有点标记(因为一个点只可能在一个环内,所以一个环内 d f s dfs dfs任意一个点都是一样的),标记过的点就不用再搜索了。

#include 
using namespace std;
typedef long long ll;

const int maxn = 200000 + 5;

int n, to, cnt, sum, ans, in[maxn], vis[maxn], head[maxn];

struct node{
	int v, next;
}e[maxn];

void addedge(int u, int v){
	e[++cnt].v = v;
	e[cnt].next = head[u];
	head[u] = cnt;
}

void toposort(){
	queue<int> q;
	for(int i = 1; i <= n; i++) if(!in[i]) q.push(i), vis[i] = 1;//入度为0删去,且不用搜索
	while(!q.empty()){
		int u = q.front();
		q.pop();
		for(int i = head[u]; i; i = e[i].next){
			if(--in[e[i].v] == 0){
				q.push(e[i].v);
				vis[e[i].v] = 1;
			}
		}
	}
}

void dfs(int x, int f){
	sum++;
	for(int i = head[x]; i; i = e[i].next){
		if(e[i].v != f) dfs(e[i].v, f), vis[e[i].v] = 1;
	}
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n;
	for(int i = 1; i <= n; i++){
		cin >> to;
		addedge(i, to);
		in[to]++;
	}
	toposort();
	ans = 0x3f3f3f3f;
	for(int i = 1; i <= n; i++){
		if(!vis[i]){
			sum = 0;
			dfs(i, i);
			ans = min(ans, sum);
		}
	}
	cout << ans << '\n';
    return 0; 
}

对于这题,你甚至可以用不压缩路径的并查集?

#include 
using namespace std;
typedef long long ll;

const int maxn = 200000 + 5;

int n, ans, cnt, pre[maxn];

int find(int x){
	++cnt;
	return x == pre[x] ? pre[x] : find(pre[x]);
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n;
	for(int i = 1; i <= n; i++) pre[i] = i;
	ans = 0x3f3f3f3f;
	for(int i = 1, x; i <= n; i++){
		cin >> x;
		cnt = 0;
		if(find(x) == i){
			ans = min(ans, cnt);
		}else{
			pre[i] = x;
		}
	}
	cout << ans << '\n';
    return 0; 
}

你可能感兴趣的:(拓扑)