POJ1236 Network of Schools(强连通分量+缩点)

3 篇文章 0 订阅
2 篇文章 0 订阅

Network of Schools

Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 20228 Accepted: 7969

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

IOI 1996

 

题意:有N个学校,从每个学校都能从一个单向网络到另外一个学校,前代可以向后代(后代的后代)发送信息。回答两个问题:

1:至少需要向多少个学校发放软件,才能使得网络内所有的学校最终都能得到软件。

2:至少需要添加几条边,才能使得任意一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。

分析:该图中的文件具有传递性。很快发现强连通的特征。对应图中的一个强连通分量,只要向其中的一个点投放文件,那么这个强连通分量就都能收到文件。将这个强连通分量缩点变为DAG(有向无环图)。这是解第一个问题的基础。在有向无环图中,边变为了强连通分量之间的文件传输关系。意味着只要一个强连通分量有入边,那么就可以通过这个入边从另外一个分量中接收文件。但是,无环图意味着肯定存在没有入度(入度为0)的强连通分量,这些强连通分量没有文件来源,所以要作为投放文件的位置。那么,第一问就只需要计算出缩点后入度为0的强连通分量数目即可。而第二个问题,把一个有向无环图转换为一个强连通分量。强连通分量的主要特征是每个点的入度和出度都不为0,那么计算出入度为0的点的个数SumIn和出度为0的点的个数SumOut,题目就变为了:在入度为0的点和出出度为0的点之间最少加多少边。很明显的可以看出,答案就是
max(SumIn,SumOut)。(注意如果该图为连通图,则不存在入度或者出度为0的点,需要单独考虑,因为这个我还wa了几次。。。)

 

代码:

 

#include<iostream>
#include<algorithm>
#include<cstring>
#include<stack>
#include<vector>
using namespace std;
const int mx = 105;
int dfn[mx],low[mx],vis[mx],num[mx],degree[mx],dg[mx];
int n,id,cnt,ans,ans1;
stack<int> s;
vector<int> mp[mx];

void init(){
    id=0;
    ans=0;
    cnt=0;
    ans1=0;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    memset(num,0,sizeof(num));
    memset(dg,0,sizeof(dg));
    memset(degree,0,sizeof(degree));
    for(int i=1;i<=n;i++)
        mp[i].clear();
    while(!s.empty())
        s.pop();
}

void tarjan(int x){
    dfn[x]=low[x]=++id;
    s.push(x);
    vis[x]=1;
    for(int i=0;i<mp[x].size();i++){
        int t=mp[x][i];
        if(!dfn[t]) {
            tarjan(t);
            low[x]=min(low[x],low[t]);
        }else if(vis[t]) low[x]=min(low[x],dfn[t]);
    }
    if(dfn[x]==low[x]){
        int tp;
        cnt++;
        do{
            tp=s.top();
            vis[tp]=0;
            num[tp]=cnt;
            s.pop();
        }while(tp!=x);
    }
}

void solve(){
    int index,sum=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<mp[i].size();j++){
            if(num[i]!=num[mp[i][j]])
                degree[num[mp[i][j]]]++,dg[num[i]]++;
        }
    }
    for(int i=1;i<=cnt;i++){
        if(!degree[i]){
            ans++;
        }
        if(!dg[i]) sum++;
    }
    if(cnt==1) {
        cout<<"1"<<endl<<"0"<<endl;
    }else {
        cout<<ans<<endl;
        ans1=max(sum,ans);
        cout<<ans1<<endl;
    }
}

int main(){
    while(cin>>n){
        init();
        for(int i=1;i<=n;){
            int a;
            cin>>a;
            if(!a) i++;
            else mp[i].push_back(a);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i])
                tarjan(i);
       solve();
    }
}

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值