学校网络

学校网络

题目描述:

一些学校连接在一个计算机网络上,学校之间存在软件支援协议,每个学校都有它应支援的学校名单(学校 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;
}
posted @ 2022-07-25 19:51  海盐味的苏打水  阅读(65)  评论(1编辑  收藏  举报