zoukankan      html  css  js  c++  java
  • [POJ 1236][IOI 1996]Network of Schools

    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


    题目大意:给定一个有向图,求至少要有多少个点, 才干从这些点出发到达全部点;至少要加入多少条边,才干从随意一点出发到达全部点

    首先要推出一个定理:在DAG中,对于全部入度不为0的点,一定有入度为0的点可达(由于从入度为0的点倒着走,一定能走到入度不为0的点)

    于是此题可用tarjan缩点,求有多少个入度为0的点,这就是第一个问题的答案。

    第二个问题的答案为入度为0的点和出度为0的点的最小值。证明比較难。略。

    对于这道题,由于仅仅要求入度和出度为0的点,故仅仅需在tarjan过程中记录每一个点归属哪个强连通分量。然后统计输出就可以

    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    
    #define MAXE 500
    #define MAXV 3000
    
    using namespace std;
    
    int N;
    
    struct edge
    {
        int u,v,next;
    }edges[MAXV];
    
    int head[MAXE],nCount=0;
    int dfn[MAXE],low[MAXE],index=0;
    int belong[MAXE],tot=0; //belong[i]=i点所属的强连通分量,tot=强连通分量总数
    bool inStack[MAXE];
    int stack[MAXE*4],top=0;
    bool map[MAXE][MAXE];
    int inDegree[MAXE],outDegree[MAXE],inZero=0,outZero=0; //入度。出度
    
    int max(int a,int b)
    {
        if(a>b) return a;
        return b;
    }
    
    int min(int a,int b)
    {
        if(a<b) return a;
        return b;
    }
    
    void AddEdge(int U,int V)
    {
        edges[++nCount].u=U;
        edges[nCount].v=V;
        edges[nCount].next=head[U];
        head[U]=nCount;
    }
    
    void tarjan(int u)
    {
        dfn[u]=low[u]=++index;
        stack[++top]=u; //该点入栈
        inStack[u]=true;
        for(int p=head[u];p!=-1;p=edges[p].next)
        {
            int v=edges[p].v;
            if(!dfn[v])
            {
                tarjan(v);
                low[u]=min(low[u],low[v]);
            }
            else if(inStack[v])
            {
                low[u]=min(low[u],dfn[v]);
            }
        }
        int v;
        if(dfn[u]==low[u])
        {
            tot++;
            do
            {
                v=stack[top--];
                belong[v]=tot;
                inStack[v]=false;
            }
            while(u!=v);
        }
    }
    
    int main()
    {
        int to;
        cin>>N;
        memset(head,-1,sizeof(head));
        for(int i=1;i<=N;i++)
        {
            while(1)
            {
                cin>>to;
                if(to==0) break;
                AddEdge(i,to);
                map[i][to]=true;
            }
        }
        for(int i=1;i<=N;i++)
            if(!dfn[i]) tarjan(i);
        for(int i=1;i<=N;i++)
            for(int j=1;j<=N;j++)
            {
                if(map[i][j]&&belong[i]!=belong[j])
                {
                    inDegree[belong[j]]++;
                    outDegree[belong[i]]++;
                }
            }
        for(int i=1;i<=tot;i++)
        {
            if(!inDegree[i]) inZero++;
            if(!outDegree[i]) outZero++;
        }
        if(tot==1) cout<<1<<endl<<0<<endl;
        else cout<<inZero<<endl<<max(inZero,outZero)<<endl;
        return 0;
    }

    
  • 相关阅读:
    java的构造方法 this 重载
    容器,组件,面板
    首先定义一个5X8的二维数组,然后使用随机数填充满。借助Arrays的方法对二维数组进行排序。
    首先创建一个长度是5的数组,并填充随机数。首先用选择法正排序,然后再对其使用冒泡法倒排序
    创建一个长度是5的数组,并填充随机数。使用for循环或者while循环,对这个数组实现反转效果
    寻找某两个数相除,其结果 离黄金分割点 0.618最近,分母和分子不能同时为偶数 * 分母和分子 取值范围在[1-20]
    密码的自动生成器:密码由大写字母/小写字母/数字组成,生成12位随机密码
    vue倒计时:天时分秒
    tbody设置超出固定的高度出现滚动条,没超出不显示。
    获取某个日期的当前周一的时间
  • 原文地址:https://www.cnblogs.com/yangykaifa/p/7294528.html
Copyright © 2011-2022 走看看