poj1236-Network of Schools

Network of Schools

Time Limit:1000MS    Memory Limit:10000KB    64bit IO Format:%I64d & %I64u

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

强连通分量果然需要强行学习……看了一晚上大神博客才看懂。

强连通分量的具体内容:点击打开链接

本题掌握了博客内容之后感觉是个模板题。

A任务要求计算最少需多少个软件才能覆盖所有学校。毫无疑问只要计算入度为0的强连通分量的数量即可,入度不为0可依靠其他学校传输。

B任务要求计算添加几个学校到相应名单能够随便找一所学校就能覆盖所有学校,即添加几条边后可以使整个图变为强连通图。

一开始我的想法是对每一个强连通分量进行分析,若入度为0,出度也为0,则需要两条边;若入度和出度只有一个为0,则加一条边;若入度和出度都不为0,不用加边。

这个想法忘记考虑到边是相互的,加了边以后很明显是两个强连通分量都加了边。而加上去的边对一个强连通分量是入度+1,对另一个强连通分量就是出度+1。这对于后续判断需要增加的边数数量有很大困难。

网上找到的解答是取入度为0和出度为0的数量之中最大的作为需要增加的边的数量。我认为这种方法的优越性在于剥离了入度与出度的相互干扰,又保证了入度与出度的绝对不为0。

事实上假设入度为0为最大值,那么对于n个入度为0的强连通分量,有选择性所增加的n条边可将n个入度为0的不相连的强连通分量逐一连接。(此时若n个入度为0的强连通分量存在相互连接,那么可以用更少的边,比如m条,将他们直接相互连接,将n-m条用于连接n个入度为0的强连通分量以外的强连通分量。)

注意,由于出度为0的个数是小于等于入度为0的个数的。假设此时为相等的情况,只需n个强连通分量都和n个以外的某一个强连通分量连接即可。假设此时为出度为0个数小于入度为0个数的情况,那么在n条边连接的n个入度为0的强连通分量中,必定存在一个强连通分量出度为1,即与n个以外的强连通分量连接,此时根据图论,已经可以得知全图为强连通图。

因此这个解答是正确的,我的证明比较粗陋,但解答的结果却无误。

下面是代码:

#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <cstdio>
#include <stack>
#include <cmath>
#include <queue>
#include <map>
#include <set>
using namespace std;
#define N 105

vector<int> e[N];
int dfn[N], low[N],stap[N], stop,belong[N],outdeg[N],indeg[N],dindex, cnt;
bool instack[N];
int n, m;

void tarjan(int x){
    int y = 0;
    dfn[x]=low[x]=++dindex;
    instack[x]=true;
    stap[++stop]=x;
    for (int i=0; i<e[x].size(); i++) {
        y=e[x][i];
        if (!dfn[y]) {
            tarjan(y);
            if (low[y]<low[x]) {
                low[x]=low[y];
            }
        }
        else if (instack[y]&&dfn[y]<low[x]){
            low[x]=dfn[y];
        }
    }
    if (dfn[x]==low[x]) {
        cnt++;
        while (y!=x) {
            y=stap[stop--];
            instack[y]=false;
            belong[y]=cnt;
        }
    }
}

void solve(){
    stop=dindex=cnt=0;
    memset(dfn, 0, sizeof(dfn));
    memset(instack, 0, sizeof(instack));
    memset(outdeg, 0, sizeof(outdeg));
    memset(indeg, 0, sizeof(indeg));
    for (int i=1; i<=n; i++) {
        if (!dfn[i]) {
            tarjan(i);
        }
    }
}

int main(){
    int temp;
    while (~scanf("%d", &n)) {
        for (int i=1; i<=n; ++i) {
            e[i].clear();
        }
        for (int i=1; i<=n; i++) {
            while (scanf("%d",&temp)) {
                if (temp==0) {
                    break;
                }
                e[i].push_back(temp);
            }
        }
        solve();
        for (int i=1; i<=n; i++) {
            int size=e[i].size();
            for (int j=0; j<size; j++) {
                if (belong[i]!=belong[e[i][j]]) {
                    outdeg[belong[i]]++;
                    indeg[belong[e[i][j]]]++;
                }
            }
        }
        int sumout=0,sumin=0;
        for (int i=1; i<=cnt; i++) {
            if (outdeg[i]==0) {
                sumout++;
            }
            if (indeg[i]==0) {
                sumin++;
            }
        }
        if (cnt==1) {
            printf("1\n0\n");
        }
        else
        printf("%d\n%d\n",sumin,max(sumin,sumout));
        
    }
    return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值