题目链接
A number of schools are connected to a computer network. Agreements have been developed among those schools: each school maintains a list of schools to which it distributes software (the “receiving schools”). Note that if B is in the distribution list of school A, then A does not necessarily appear in the list of school B
You are to write a program that computes the minimal number of schools that must receive a copy of the new software in order for the software to reach all schools in the network according to the agreement (Subtask A). As a further task, we want to ensure that by sending the copy of new software to an arbitrary school, this software will reach all schools in the network. To achieve this goal we may have to extend the lists of receivers by new members. Compute the minimal number of extensions that have to be made so that whatever school we send the new software to, it will reach all other schools (Subtask B). One extension means introducing one new member into the list of receivers of one school.
Input
The first line contains an integer N: the number of schools in the network (2 <= N <= 100). The schools are identified by the first N positive integers. Each of the next N lines describes a list of receivers. The line i+1 contains the identifiers of the receivers of school i. Each list ends with a 0. An empty list contains a 0 alone in the line.
Output
Your program should write two lines to the standard output. The first line should contain one positive integer: the solution of subtask A. The second line should contain the solution of subtask B.
Sample Input
5
2 4 3 0
4 5 0
0
0
1 0
Sample Output
1
2
题意: 有n个学校,他们之间可以相互发送软件,但是有些学校之间不能相互到达,计算最少需要多少个学校发送软件,才能使全部学校都能收到软件。同时,至少建立多少条边,才能使所有学校相互到达。
思路:
因为图是有向图,所以对于入度为0的点,没有其他点能够到达,所以入度为0的点即为需要发送软件的学校。
由于有些学校是不能相互到达的,添加最少的边使得他们之间能够相互到达,即为一个有向图,求在图中最少要加多少条边能使得该图变成一个强连通图。所以,我们可以求出缩点后,入度为0的点的个数a,出度为0的点的个数b,至少添加max(a,b)个边,能够使得他们变成强连通图。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAXN = 11000;
struct Edge
{
int v;
int next;
}e[MAXN];
int head[MAXN];
int dfn[MAXN],low[MAXN];
bool vis[MAXN];
int stact[MAXN];
int belong[MAXN],num[MAXN];
//belong表示每个点属于的缩完之后的哪一个点,num表示每一个缩点里面有多少个点
int inde[MAXN];//表示每个缩点的入读
int outde[MAXN];//每个缩点的出度
int cnt,tot,index,now;
int n;
void add(int u,int v)
{
e[cnt].next = head[u];
e[cnt].v = v;
head[u] = cnt++;
}
void Tarjan(int x)
{
low[x] = dfn[x] = ++tot;
vis[x] = 1;
stact[++index] = x;
for(int i = head[x];i != -1; i = e[i].next)
{
int v = e[i].v;
if(!dfn[v])
{
Tarjan(v);
low[x] = min(low[x], low[v]);
}
else if(vis[v])
low[x] = min(low[x], dfn[v]);
}
if(low[x] == dfn[x])
{
++now;
do
{
vis[stact[index]] = 0;
belong[stact[index]] = now;
num[now] ++;
index --;
}while(x != stact[index+1]);
}
return ;
}
void init()
{
for(int i = 0;i<=n;i++){
head[i] = -1;
num[i] = belong[i] = dfn[i] = low[i] = 0;
inde[i] = outde[i] = 0;
vis[i] = false;
}
cnt = tot = index = now = 0;
}
int main()
{
scanf("%d",&n);
init();
int x;
for(int i=1;i<=n;i++){
while(scanf("%d",&x),x)
add(i,x);
}
for(int i = 1;i <= n;i ++)
if(!dfn[i])
Tarjan(i);
//缩点完成之后,我们就一定没有环的存在
int u,v;
for(int i = 1;i <= n;i ++){
for(int j = head[i];~j;j=e[j].next){
int v = e[j].v;
//表示如果这条边不在缩点之内,那么就是用来连接缩点
if(belong[i] != belong[v]){
inde[belong[v]] ++;
outde[belong[i]] ++;
}
}
}
int a = 0,b = 0;
//分别计算所的缩点中,入度和出度为0的数目
for(int i = 1;i <= now;i ++)
{
if(!inde[i]) a++;
if(!outde[i]) b++;
}
printf("%d\n",a);
if(now == 1)
//如果所有的缩点只有一个,则不需要添加新边
printf("0\n");
else
printf("%d\n",max(a,b));
}