POJ 1236(强连通分量)

题目链接 http://poj.org/problem?id=1236

翻译一下题目吧,,,,

大致含义就是,有n个学校,现在要向n个学校传递一个软件,如果A学校愿意支援B学校,那么给了A,A就会给B,但是A支援B但是B不一定支援A (有向图警告)  ,要求什么呢,最少给多少个学校就可以给到全部的学校,最少加几个支援关系,可以使得给任意一个学校就可以传递到全部学校去。

思路:

第一个问题吧,首先同一个强连通分量内的随意给一个学校就可以,因此可以将一个强连通分量看作是一个点处理 (缩点) ,缩点后,重新建图,一个点代表一个强连通分量,不在一个强连通分量内的,如果A中有一个点可以到达B内的一个点,那么相当于两个强连通分量有边连接A --> B,对于这样的关系,我们发现,只要给了A,A就会给B,也就是说,对于这样的一个链,只要给了第一个后面的就都有了,第一个怎么表示呢???对于A --> B -->C,发现,B和C都有入度,也就是说B和C都会有学校传递给他们,就不用管了,对于A而言,它没有入度,也就是说,如果你不给他,他就不可能会有的。所以只要统计入度为0的点的个数即可。

对于第二个问题,ahhhh,我还想了一小会儿呢,相当于将整个图的点,添加几条边后变成一个强连通分量,强连通分量内部的点一定满足每个点至少一个入度和一个出度,那么对于图中没有入度的点补上入度,没有出度的点,补上出度就可以构成一个强连通分量。要想最少话,肯定是自给自足了,就是把入度为0的点和出度为0的点连边,比如现在有n个点出入为0,有n个点入度为0,将这n个出度为0的点分别向n个出度为0的点连边,这样就补全了出度和入度为0的点,如果出度和入度为0的点数量不同,假设入度为0有m个,出度为0有n个,n > m,肯定还要补的首先从n个点中选m个与出度为0的点连边,还剩下n-m个点出度为0,只需要将这n-m个点向前面的n个补了入度的点连边就行了,如果入度大于出度,将原本出度为0的点多向入度为0的点连边就行了,所以答案应该是max(n, m)

特别地,如果只有一个强连通分量,第二个就不用统计了,直接是0就可以了。

/**
 * Author : correct
 */
#include <iostream>
#include <cstdio>
#include <cstring>
#define mem(a, b) memset(a, b, sizeof a)
using namespace std;
const int N = 10100;
int head[N], nex[N], to[N], cnt;
void add(int a, int b){
	++cnt;
	to[cnt] = b;
	nex[cnt] = head[a];
	head[a] = cnt;
}
int n;
int dfn[110], low[110];
bool vis[110];
int block[110];// 记录属于哪一个强连通分量
int st[110], top;
int num, q;
void pre_work(){
	mem(head, -1);
	mem(nex, - 1);
	cnt = 0;
	num = 0;
	mem(dfn, 0);
	mem(low, 0);
	mem(vis, 0);
	top = 0;
	q = 0;
	mem(block, 0);
}
void tarjan(int x){
	dfn[x] = low[x] = ++num;
	st[++top] = x;
	vis[x] = 1;
	for (int i = head[x]; ~i; i = nex[i]){
		int y = to[i];
		if (dfn[y] == 0){
			tarjan(y);
			low[x] = min(low[x], low[y]);
		}
		else if (vis[y])low[x] = min(low[x], dfn[y]);
	}
	if (low[x] == dfn[x]){
		++q;
		while (1){
			int t = st[top--];
			vis[t] = 0;
			block[t] = q;
			if (t == x)break;
		}
	}
}
bool c[110];
bool out[110];
int main(){
	// 1.统计入读为0的点的个数
	// 2.出度为0和入度为0点数的最大值,特别地,只有一个强连通分量的时候是0
	pre_work();
	ios::sync_with_stdio(false);
	cin >> n;
	for (int i = 1; i <= n; i++){
		int x;
		while (cin >> x && x){
			add(i, x);
		}
	}
	for (int i = 1; i <= n; i++){
		if (dfn[i] == 0){
			tarjan(i);
		}
	}
	mem(c, 0);
	mem(out, 0);
	for (int i = 1; i <= n; i++){
		for (int j = head[i]; ~j; j = nex[j]){
			int y = to[j];
			if (block[i] != block[y]){
				c[block[y]] = 1;// 入度
				out[block[i]] = 1;// 出度
			}
		}
	}
	int ans = 0;
	int chu, ru;
	chu = 0;
	ru = 0;
	for (int i = 1; i <= q; i++){
		if (!c[i])++ans;
		if (!c[i])ru++;
		if (!out[i])chu++;
	}
	cout << ans << "\n";
	cout << (q == 1 ? 0 : max(chu, ru)) << "\n";
	return 0;
}
发布了204 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43701790/article/details/104795964