zoukankan      html  css  js  c++  java
  • NYOJ120 校园网络 强连接

    校园网络

    时间限制:3000 ms  |  内存限制:65535 KB
    难度:5
    描述

    南阳理工学院共有M个系,分别编号1~M,其中各个系之间达成有一定的协议,如果某系有新软件可用时,该系将允许一些其它的系复制并使用该软件。但该允许关系是单向的,即:A系允许B系使用A的软件时,B未必一定允许A使用B的软件。

    现在,请你写一个程序,根据各个系之间达成的协议情况,计算出最少需要添加多少个两系之间的这种允许关系,才能使任何一个系有软件使用的时候,其它所有系也都有软件可用。

    输入
    第一行输入一个整数T,表示测试数据的组数(T<10)
    每组测试数据的第一行是一个整数M,表示共有M个系(2<=M<=100)。
    随后的M行,每行都有一些整数,其中的第i行表示系i允许这几个系复制并使用系i的软件。每行结尾都是一个0,表示本行输入结束。如果某个系不允许其它任何系使用该系软件,则本行只有一个0.
    输出
    对于每组测试数据,输出最少需要添加的这种允许关系的个数。
    样例输入
    1
    5
    2 4 3 0
    4 5 0
    0
    0
    1 0
    样例输出
    2
    代码:
    View Code
    #include<stdio.h>
    #include<stack>
    #include<string.h>
    using namespace std;
    #define M 100100
    #define N 5000//N为最大顶点个数,M为最大有向边数
    stack<int>s;
    
    int n,m,t; //n为顶点个数,m为有向边数,t为时间戳
    int con,belong[N]; //con为强连通分量个数,belong用来缩点的数组
    int head[N],next[M],to[M]; //边表形式存储图结构,下标从1开始
    int dfn[N]; // 保存顶点i搜索的次序(时间戳)编号
    int low[N]; // 保存顶点i或i的子树最早的次序编号
    bool flag[N]; //保存顶点是否在堆栈中
    int min(int a,int b)
    {
        return a<b?a:b;
    }
    
    void add(int u,int v)//添加从u到v的有向边
    {
        to[m]=v;
        next[m]=head[u];
        head[u]=m++;
    }
    
    void tarjan(int u)//从顶点u出发深度搜索
    {
        int v,p;
        t++;//时间戳
        dfn[u]=low[u]=t;
        s.push(u);//入堆栈
        flag[u]=true;//标记堆栈中有u
        for(p=head[u];p>0;p=next[p])
        {
            v=to[p];
            if(dfn[v]==0)//to[p],即点顶v没有扫过
            {
                tarjan(v); //深搜
                low[u]=min(low[u],dfn[v]);//找树的尽可能小的根
            }
            else if(flag[v]) //to[p]扫过且to[p]在堆栈中
                low[u]=min(low[u],dfn[v]);
        }
        if(dfn[u]==low[u]) //顶点u是强连通分量的根
        {
            int tmp=-1;
            con++;  //强连通分量的个数增加1
            while(tmp!=u)  //缩点
            {
                tmp=s.top();
                s.pop();
                belong[tmp]=con;
                flag[tmp]=false;
            }
        }
    }
    
    
    void solve()  //求解连通分量个数并缩点
    {
        for(int i=1;i<=n;++i)
            if(dfn[i]==0)
                tarjan(i);
    }
    
    void contract()  //统计入度为0和出度为0的强连通分量个数
    {
        int innum=0,outnum=0,i,j;
        bool in[N],out[N];
        memset(in,0,sizeof(in));
        memset(out,0,sizeof(out));
        for(i=1;i<=n;i++)
            for(j=head[i];j>0;j=next[j])
                if(belong[i]!=belong[to[j]]) //顶点i和顶点to[j]不在同一个强连通分量中
                {
                    out[belong[i]]=1;
                    in[belong[to[j]]]=1;
                }
        for(i=1;i<=con;++i)//扫描每个强连通分量
        {
            if(in[i]==0)
                innum++;
            if(out[i]==0)
                outnum++;
        }
        if(con==1)
            printf("0\n");
        else
            printf("%d\n",innum>outnum?innum:outnum);
    }
    
    
    
    
    int main()
    {
        int T,a;
        scanf("%d",&T);
        while(T--)
        {
            while(!s.empty())
                s.pop(); // 清空堆栈
            memset(head,0,sizeof(head));
            memset(next,0,sizeof(next));
            memset(to,0,sizeof(to));
            memset(dfn,0,sizeof(dfn));
            //memset(flag,false,sizeof(flag));
            m=1;
            t=0;
            con=0;
            scanf("%d",&n);
            for(int i=1;i<=n;++i)
            {
                while(scanf("%d",&a),a)
                   add(i,a);
            }
            solve();
            contract();
        }
        return 0;
    }

    思路:用强连接 统计出 强连接分量,在找 出度个入度最大的,就是需要添加的边。

    PS:这不是一个完美的答案,因为是存在纰漏的

  • 相关阅读:
    C++初识
    实现小程序iphonex与iphone6底部适配
    vue-element实现外部独立全选
    flex中图片元素被挤压问题
    vue+element-Ui实现简单的表单必填项验证(1)
    Vuex——项目应用(1)
    Vue项目小应用
    transtion过渡
    axios的高阶用法
    阿里云服务器初步使用介绍
  • 原文地址:https://www.cnblogs.com/zibuyu/p/2956045.html
Copyright © 2011-2022 走看看