学校网络
学校网络
题目描述:
一些学校连接在一个计算机网络上,学校之间存在软件支援协议,每个学校都有它应支援的学校名单(学校 A 支援学校 B,并不表示学校 B 一定要支援学校 A)。当某校获得一个新软件时,无论是直接获得还是通过网络获得,该校都应立即将这个软件通过网络传送给它应支援的学校。因此,一个新软件若想让所有学校都能使用,只需将其提供给一些学校即可。现在请问最少需要将一个新软件直接提供给多少个学校,才能使软件能够通过网络被传送到所有学校?最少需要添加几条新的支援关系,使得将一个新软件提供给任何一个学校,其他所有学校就都可以通过网络获得该软件?
输入格式
第 1 行包含整数 N,表示学校数量。
第 2…N+1 行,每行包含一个或多个整数,第 i+1 行表示学校 i 应该支援的学校名单,每行最后都有一个 0 表示名单结束(只有一个 0 即表示该学校没有需要支援的学校)。
输出格式
输出两个问题的结果,每个结果占一行。
数据范围
2≤N≤100
输入样例:
5
2 4 3 0
4 5 0
0
0
1 0
1
2
3
4
5
6
输出样例:
1
2
思路
Tarjan模板题,Tarjan缩点将原图转化成 DAG(有向无环图),统计每个强连通分量的出度入度,记录起点数量,终点数量。对于一个强连通分量,其中只要有一所学校获得新软件那么整个分量都能获得。所以只要把软件给所有起点即可,答案为起点个数 。对于第二个问题,如果只有一个强连通分量,那么答案为0,因为全在一个强连通分量中,不需要填边就能随意传输,如果强连通分量大于1,最后答案为max(出度为0的数量,入度为0的数量)。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=110,M=100010;
int dfn[N],low[N],timetemp; //dfn[i]表示搜到i点的时间戳,low[i]表示i点能回溯到的时间戳
int h[N],e[M],ne[M],idx; //链式前向星
int id[N]; //表示i点所在的连通分量
int din[N]; //缩点以后的入度
int dout[N]; //缩点以后的出度
int n;
stack<int> st;
bool inst[N];
int num;
void add(int a,int b)
{
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void Tarjan(int u)
{
dfn[u]=low[u]=++idx;
st.push(u),inst[u]=true;
for(int i=h[u];i!=-1;i=ne[i]) {
int j=e[i];
if(!dfn[j]) { //如果没有被搜过
Tarjan(j);
low[u]=min(low[u],low[j]);
}
else if(inst[j]) low[u]=min(low[u],dfn[j]); //如果被搜过了并且在栈中
}
if(dfn[u]==low[u]) { //同一个连通分量的出栈
++num;
int now;
do{
now=st.top();
st.pop();
inst[now]=false;
id[now]=num;
}while(u!=now);
}
}
int main()
{
memset(h,-1,sizeof(h));
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]) Tarjan(i);
}
for(int i=1;i<=n;i++) { //遍历连边
for(int j=h[i];j!=-1;j=ne[j]) {
int k=e[j];
int a=id[i],b=id[k];
if(a!=b) {
dout[a]++;
din[b]++;
}
}
}
int a=0,b=0;
for(int i=1;i<=num;i++) {
if(din[i]==0) a++;
if(dout[i]==0) b++;
}
cout<<a<<endl;
if(num==1) cout<<0<<endl;
else cout<<max(a,b)<<endl;
// system("pause");
return 0;
}