P2746 [USACO5.3]校园网Network of Schools

题目描述

一些学校连入一个电脑网络。那些学校已订立了协议:每个学校都会给其它的一些学校分发软件(称作“接受学校”)。注意即使 B 在 A 学校的分发列表中, A 也不一定在 B 学校的列表中。

你要写一个程序计算,根据协议,为了让网络中所有的学校都用上新软件,必须接受新软件副本的最少学校数目(子任务 A)。更进一步,我们想要确定通过给任意一个学校发送新软件,这个软件就会分发到网络中的所有学校。为了完成这个任务,我们可能必须扩展接收学校列表,使其加入新成员。计算最少需要增加几个扩展,使得不论我们给哪个学校发送新软件,它都会到达其余所有的学校(子任务 B)。一个扩展就是在一个学校的接收学校列表中引入一个新成员。

输入输出格式

输入格式:

 

输入文件的第一行包括一个整数 N:网络中的学校数目(2 <= N <= 100)。学校用前 N 个正整数标识。

接下来 N 行中每行都表示一个接收学校列表(分发列表)。第 i+1 行包括学校 i 的接收学校的标识符。每个列表用 0 结束。空列表只用一个 0 表示。

 

输出格式:

 

你的程序应该在输出文件中输出两行。

第一行应该包括一个正整数:子任务 A 的解。

第二行应该包括子任务 B 的解。

 

输入输出样例

输入样例#1: 复制

5
2 4 3 0
4 5 0
0
0
1 0

输出样例#1: 复制

1
2

说明

题目翻译来自NOCOW。

USACO Training Section 5.3

 

思路:这个题目是要是用链式前向星+tarjan+缩点+强连通来解决的,咱们先来看看这道题的思路,简单来说这俩问第一问求的是缩点后入度为0的点的个数,第二问求得是缩点后Max(入度为0的数目,出度为0的数目),下面来解释下原因,对于第一问,要使图连通必须从入度为0的点开始,所求为其个数,第二问则是最少添加多少条边可以使图为为强连通图,可以知道只要把所有的出度为0的点和入度为0的点俩俩相连便可,即求俩者最大值,记住特判,因为若是开始给出的图为强连通图,那么缩点后max(1,1) = 1明显不符合题意,下面看代码

 

#include<iostream> 
using namespace std;
const int maxn = 1100;
int cnt = 1, n, total, sum, index, ans1, ans2, ans, k;
int head[maxn], outdu[maxn], indu[maxn], dfn[maxn], low[maxn], Stack[maxn], vis[maxn], belong[maxn];
struct Node{
	int to;
	int next;
}node[2 * maxn];

void add(int u, int v) {
	node[cnt].to = v;
	node[cnt].next = head[u];
	head[u] = cnt++;
}

void tarjan(int u) {
	dfn[u] = low[u] = ++total;
	Stack[++index] = u;
	vis[u] = 1;
	for (int i = head[u]; i != 0; i = node[i].next) {
		int v = node[i].to;
		if (!dfn[v]) {
			tarjan(v);
			low[u] = min(low[u], low[v]);
		} else if (vis[v]) {
			low[u] = min(low[u], dfn[v]);
		}
	}
	if (low[u] == dfn[u]) {
		++sum;
		do {
			int p = Stack[index--];
			vis[p] = 0;
			belong[p] = sum;
		} while (Stack[index + 1] != u);
	}
}

int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		while (cin >> k && k) {
			add(i, k);
		}
	}
	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 != 0; j = node[j].next) {
			int v = node[j].to;
			if (belong[v] != belong[i]) { //判断是不是同一个缩点
				indu[belong[v]]++; //入度
				outdu[belong[i]]++; //出度
			}
		}
	}
	for (int i = 1; i <= sum; i++) {
		if (!indu[i]) ans1++;
		if (!outdu[i]) ans2++;
	}
	ans = max(ans1, ans2);
	if (sum == 1) {
		cout << 1 << endl;
		cout << 0;
	} else {
		cout << ans1 << endl;
		cout << ans << endl;
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值