Network of Schools
Description
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 Source |
[Submit] [Go Back] [Status] [Discuss]
[题意][有N个学校...每个学校可能向几个学校进行数据传输(单向的)问..至少需要把一个文件给几个学校可以使给的N个学校都收到文件...再问在加几个通信线路可以使各个学校之间都能直接或间接的传递文件]
【题解】【一个强连通分量里的所有点可以互相传输,那么这道题实际上就是在求当前图中有几个强连通分量,第二问是问还需再加入几条边,也就是说还需要几条边把剩下的点连起来。因为一个强连通分量必然不需要考虑,所以考虑把每个强连通分量缩成一个点来处理】
【求强连通分量要用到tarjan算法】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[100500],nxt[100500],p[110],tot;//next数组存有向图
int dft[110],dis[110],root,cnt;//dst表示dfs到当前点的时间,dis表示当前点可到达的点中时间最小的
int in[110],out[110],f[110],n;//点的入度和出度,f是缩点后的每个点的序号
int d[110],top;//栈
bool vis[110];//记录每个元素是否在栈中
inline void add(int x,int y)
{
tot++; a[tot]=y; nxt[tot]=p[x]; p[x]=tot;
}
void tarjan(int u)
{
dft[u]=dis[u]=++cnt;
d[++top]=u; vis[u]=1;
int v=p[u];
while(v>=0)
{
if(!dft[a[v]])
{
tarjan(a[v]);
dis[u]=min(dis[u],dis[a[v]]);
}
else
if(vis[a[v]]&&dis[u]>dft[a[v]]) dis[u]=dft[a[v]];
v=nxt[v];
}
int b=a[p[u]];
if(dft[u]==dis[u])//说明找到一个强连通分量
{
++root;//缩点
do{
b=d[top--];
vis[b]=false;
f[b]=root;
}while(u!=b);//把当前强连通分量里包含的所有点缩成一个点来代替
}
return;
}//查找强连通分量
void solve()
{
root=top=cnt=0;
memset(dft,0,sizeof(dft));
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;++i)
if(!dft[i]) tarjan(i);
return;
}//dfs找所有的强连通分量,每次从一个没有搜索过的点开始
void count()
{
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
for(int i=1;i<=n;++i)
{
int u=p[i];
while(u>=0)
{
if(f[i]!=f[a[u]])
out[f[i]]++,in[f[a[u]]]--;
u=nxt[u];
}
}
return;
}//缩点后,看还有几个点不属于强连通分量,则统计入度和出度,以便于后面计算要新加几条边
int main()
{
int i,j;
while((scanf("%d",&n)==1)&&n)
{
tot=0;
memset(nxt,-1,sizeof(nxt));
memset(p,-1,sizeof(p));
int b;
for(i=1;i<=n;++i)
while((scanf("%d",&b)==1)&&b) add(i,b);
solve();
if(root==1) {printf("1\n0\n"); continue;}//如果缩完后就剩下一个点,即不需要再添边,直接输出即可
count();
int inr=0,outr=0;
for(i=1;i<=root;++i)
{
if(!in[i]) inr++;
if(!out[i]) outr++;
} //看缩点后有几个单独的点,就要新加几条边
printf("%d\n%d\n",inr,max(inr,outr));
}
return 0;
}
(Tarjan算法见上一篇博文)