tarjan校园网

思路分析:如果某几个学校之间相互可达(强连通分量的定义),则可以将其视为一个点。这里就要使用一种叫做Tarjan的算法来找出图中所有的强连通分量。这一过程称为缩点。

缩点以后,可以保证图里没有环,只有链。由于不存在环,每条链必然存在一个唯一的入度为0的点。显然,对于每条链,只需要将软件发给这个入度为0的点,就可以间接传给这条链上所有的点。因此,任务A就转化为了求缩点后的图中有多少个点入度为0

下面来看任务B。我们发现,图中只要存在入度为0的点和出度为0的点就永远不可能满足要求:“ 不论我们给哪个学校发送新软件,它都会到达其余所有的学校 ”。我们还发现,只要在入度为0的点和出度为0 的点之间连一条边,就可以同时消灭两个“不合法”的点。如果不能做到刚好两两配对(不妨假设入度为0的点多),就给每个多出来的入度为0的点随便找一个出度为0的点配对(也就是说一个点可以同时配多个点)。因此,入度为0的点数与出度为0的点数的较大值即为任务B的答案。

本题注意点:

1.给强连通分量之间连边时可能会重复连,导致重复计算入度出度。因此用一个set(而不是习惯上的邻接表vector)来存缩点以后的图。

2.如果一共只有一个点要特判(这个点不需要自己和自己配对)。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxm = 500;
struct sch {
	int to;
	int next;
} edge[maxm];
int n, nin, nou, cnt, s;
int head[maxm];
int dfn[maxm], low[maxm], co[maxm], q[maxm];
int fin[maxm], fou[maxm];
int num, col, top;
void add(int u, int v) {
	cnt++;
	edge[cnt].to = v;
	edge[cnt].next = head[u];
	head[u] = cnt;
}
void tarjan(int u) {
	dfn[u] = low[u] = ++num;
	q[++top] = u;
	for (int i = head[u]; i; i = edge[i].next) {
		int v = edge[i].to;
		if (!dfn[v]) {
			tarjan(v);
			low[u] = min(low[u], low[v]);
		}
	    else if(!co[v])
	        low[u] = min(low[u], low[v]);
	}
	if (dfn[u] == low[u]) {
		s++;
		co[u] = ++col;
		while (q[top] != u) {
			co[q[top]] = col;
			top--;
		} 
		top--;
	}
	
}
int main() {
	int x = 1;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) 
		while (scanf("%d", &x)&&x != 0) 
			if (x != 0) add(i, x);
	for (int i = 1; i <= n; i++) 
		if (!dfn[i]) tarjan(i);
	for (int i = 1; i <= n; i++) 
		for (int j = head[i]; j; j = edge[j].next) {
			int v = edge[j].to;
			if(co[i] != co[v]) {
				fin[co[v]]++;
				fou[co[i]]++;
			}  
		}
	for (int i = 1; i <= col; i++) {
		if (fou[i] == 0) nou++;
		if (fin[i] == 0) nin++;
    }
    if (s == 1) cout<<1<<endl<<0<<endl;
    else {
    	printf("%d\n", nin);
	    printf("%d\n", max(nin, nou));
	}
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/syh8501/article/details/85392039