zoukankan      html  css  js  c++  java
  • POJ-1236-Network of Schools(强连通图,找缩点,判断入度出度)

    Network of Schools
    Time Limit: 1000 MS Memory Limit: 10000 KB

    64-bit integer IO format: %I64d , %I64u Java class name: Main

    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

    题意:给很多个学校装软件,学校之间有网络通路相互连接可以传递安装包,这些学校的网络是有向传递的。输入学校数目N,然后接下来N行,分别是从1开始的第i个学校与哪些学校之间存在传递通路,以0结束输入。
    需要输出的问题有两个
    1、至少要给几个学校初始安装包,就可以通过学校间的传递,给所有学校安装上软件。也就是判断将图化为缩点后,所有缩点的个数
    2、加上几条边后,可以使所有学校相互连通,也就是化为缩点后,将所有缩点加上几条边可以使图变成强连通图。判断强联通分量中入度为0和出度为0的点谁多,就要加上几条边。
    也就是要将图中的强联通分量化为缩点,几个缩点就是几个入度0的点,把缩点都连接为一个强连通图,就是 问题2 的答案

    #include<stdio.h>
    #include<string.h>///有向无环图DAG   求缩点
    #include<stack>
    #include<algorithm>
    using namespace std;
    int n,low[108],dfn[108],cnt,out,in,num,id[108];///low数组用于存储最小出生时间,id存储节点属于哪个强连通图
    bool maps[108][108],vis[108];///maps用于邻接矩阵构造连通图,vis用于标记节点是否在栈中
    stack<int>q;
    void tarjan(int u)///tarjan算法
    {
        int i;
        low[u]=dfn[u]=++cnt;
        q.push(u);                           ///入栈
        vis[u]=true;                         ///标记u节点在栈中
        for(i=1; i<=n; i++)
        {
            if(!maps[u][i])continue;
            if(!dfn[i])
            {
                tarjan(i);
                low[u]=min(low[u],low[i]);
            }
            else if(vis[i])///注意这里是在栈中的点才用于更新u几点low值
            {
                low[u]=min(low[u],dfn[i]);
            }
        }
        if(low[u]==dfn[u])///判断强连通图的条件,当low值等于dfu值时,说明此时为  深搜子树的根节点
        {
            num++;///强联通图个数计数
            while(!q.empty())///此时栈中存储的是强联通图的所有节点
            {
                int tmp=q.top();
                q.pop();///出栈时应该取消节点的标记
                id[tmp]=num;
                vis[tmp]=0;///取消标记
                if(tmp==u)///直到寻找到栈顶中的点tmp等于正在遍历的点u时,这个强联通图的所有点寻找完毕
                {
                    break;
                }
            }
        }
    }
    int main()
    {
        int i,inum[108],outnum[108],j;
        while(scanf("%d",&n)!=EOF)
        {
            memset(vis,false,sizeof(vis));
            memset(maps,false,sizeof(maps));
            memset(low,0,sizeof(low));
            memset(dfn,0,sizeof(dfn));
            memset(inum,0,sizeof(inum));
            memset(outnum,0,sizeof(outnum));
            memset(id,0,sizeof(id));
            while(!q.empty())q.pop();
            cnt=out=in=num=0;
            for(i=1; i<=n; i++)
            {
                int u;
                while(scanf("%d",&u)&&u)
                {
                    maps[i][u]=true;
                }
            }
            for(i=1; i<=n; i++) ///求缩点都是这样咯,每个点都要搜一次,之前搜的时候走过的就不用再搜了
            {
                if(!dfn[i])
                {
                    tarjan(i);///这里要遍历所有点,因为这个图不一定是所有点的都连通的
                }
            }///深搜完后,就已经找到了所有强联通分量了
    //        for (i=1; i<=n; ++i)
    //            printf("%3d",id[i]);
    //        puts("");
            for(i=1; i<=n; i++)///这里对每个点的连接进行遍历
            {
                for(j=1; j<=n; j++)
                {
                    if(maps[i][j]&&id[i]!=id[j])///如果两点有连接,并且i点与j点不属于一个强联通分量
                    {
                        inum[id[j]]++;         ///j点作为入度,相当于i所在的强联通分量 有方向的 走向j所在强联通分量
                        outnum[id[i]]++;       ///i是出度
                    }
                }///这里进行所有的强联通分量的入度和出度计数,第i个强联通分量的入度在inum数组中,出度位于outnum数组中
            }
            for(i=1;i<=num;i++)///最后统计一下,所有强联通分量的0入度和0出度的个数
            {
                if(!inum[i])in++;
                if(!outnum[i])out++;
            }
            if(num==1)///如果整个图只有一个强联通分量,那么就只有一个出度为0和一个入度为0的点
            {
                printf("1
    0
    ");///要特判,只用发一个软件即可,并且不用加边
            }
            else
            {
                printf("%d
    %d
    ",in,max(in,out));///否则就是,发放软件数量是0入度的点的数量(根节点),需要加的边数,则是将0入度的连接0出度的,谁大结果就是几条边
            }
        }
        return 0;
    }
    
  • 相关阅读:
    Oracle中查看建立索引和使用索引的注意点
    一个父亲的教育札记——leo鉴书58
    puma 配置,启动脚本
    HDU 6003 Problem Buyer
    c# 类间关系
    前台线程和后台线程总结
    多线程学习进程
    进程类的使用
    c#异步编程
    【程序17】
  • 原文地址:https://www.cnblogs.com/kuronekonano/p/11794352.html
Copyright © 2011-2022 走看看