1236 Network of Schools 图的强连通性

Network of Schools
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 3010 Accepted: 1160

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

 

各学校之间有单向的网络。如果 A--->B 则如果 A 得到一个软件则可以给学校 B,在这样一个有向图中,有两个问题。
1, 至少要向多少个学校提供软件,可使所有学校都得到软件。
2,至少需要几条边,使得该图变为一个强连通图。

首先求出强连通分支,缩点,之后得到的图,看看这个图有多少个节点的入度为 0,这样的节点数目就是需要提供软件的学校数目,因为不然的话,由于入度为 0,它们会被孤立掉。
第二个问题:设有向图入度,出度为 0 的节点数分别为 Iz,Oz,则通过给它加边,使它变成强连通图,最少要加 max{Iz,Oz} 条边。

【分析】:
第一问很明显是在求最小点基:
下面是一些相关的知识


点基B满足:
    对于任意一个顶点Vj,一定存在B中的一个Vi,使得Vi是Vj的前代。
点基包含:顶点数最小的点基 权最小的点基(当所有的权为1时,权最小的点基变成了顶点数的最小的点基)
最高强连通分支:
    定义:如果在G中,不存在终点属于[Si]而起点不属于[Si]的弧,就称[Si]为最高强连通分支。
    性质:强连通分支Si与图G其他部分相连的所有弧都是向外伸展的。即表明任何非Si中的点与Si中的点没有有向边。说明,最小点基必须在强连通分支中取一个。所以最小点基的个数即为强连通分支的个数。

对于第二问:
一个图是强连通分量的充分条件是图中没有入度为0的点,或出度为0的点,所以我们只要把图中入度和出度为0的点连起来使入度为0的点和出度为0的点消失即可。那么这样最少要连的边就是max(入度为0的点的个数,出度为0的点的个数);
不过需要注意的是:当图被缩成一个点的时候,第一问为1,第二问为0


 

#include<stdio.h>

#define MAX_SIZE 20008

int stack[MAX_SIZE];

int in[MAX_SIZE],out[MAX_SIZE];//题目中要求记录出度和入度的数组

bool instack[MAX_SIZE];

int N,b_cnt,idx,top;

struct Edge

{

    int adj;//adj

    Edge* next;//point to next

};

struct Node

{

    int DFN,LOW;//定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。

    int belongs;//belongs to B_cnt,属于哪一个强联通分支

    Edge *first;//point to first,形成链状

} node[MAX_SIZE];

void Init() //initial node[],in[],out[]

{

    for(int i=0; i<=N; i++)

    {

        node[i].first=NULL;

        node[i].DFN=node[i].LOW=0;

        node[i].belongs=0;

        instack[i]=false;

        in[i]=out[i]=0;//不在模板中

    }

}

void InsertEdge(int u,int v) //insert edge u->v

{

    Edge*p=new Edge;

    p->adj=v;//指向的下一个节点

    p->next=node[u].first;

    node[u].first=p;

}

void Tarjan(int u) //Tarjan

{

    int v;

    node[u].DFN=node[u].LOW=(++idx);

    instack[u]=true;

    stack[++top]=u;

    for(Edge*p=node[u].first; p; p=p->next)

    {

        v=p->adj;

        if(!node[v].DFN)

        {

            Tarjan(v);

            if(node[v].LOW<node[u].LOW)

                node[u].LOW=node[v].LOW;

        }

        else if(instack[v]&&node[v].DFN<node[u].LOW)

            node[u].LOW=node[v].DFN;

    }

    if(node[u].DFN==node[u].LOW)

    {

        b_cnt++;

        do

        {

            v=stack[top--];

            instack[v]=false;

            node[v].belongs=b_cnt;

        }

        while(u!=v);

    }

}

void Tarjan_SCC() //StronglyConnectedComponent

{

    int i,t1,t2;

    b_cnt=idx=top=0;//b_cnt 新图的点数

    for(i=1; i<=N; i++)

        if(!node[i].DFN)

            Tarjan(i);

    if(b_cnt<=1)

        printf("1/n0/n");

    else

    {

        for(i=1; i<=N; i++)

            for(Edge*p=node[i].first; p; p=p->next)

                if(node[i].belongs!=node[p->adj].belongs)

                    in[node[i].belongs]++,out[node[p->adj].belongs]++;

        t1=t2=0;

        for(i=1; i<=b_cnt; i++)

        {

            if(!in[i])t1++;

            if(!out[i])t2++;

        }

        printf("%d/n",t2);

        printf("%d/n",t1>t2?t1:t2);

    }

}

int main()

{

    int i,u,v,x;

    while(scanf("%d",&N)!=EOF)

    {

      Init();

      for(int i=1;i<=N;i++)

      {

         while((scanf("%d",&x),x)!=0)

            InsertEdge(i,x);

      }

      Tarjan_SCC();

    }

    return 0;

}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值