• NYOJ120 校园网络(强连通缩点targan算法)


     

    校园网络

    时间限制: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
    来源
    POJ改编
    解析(转):
    Tarjan算法详解

    【功能】

          Tarjan算法的用途之一是,求一个有向图G=(V,E)里极大强连通分量。强连通分量是指有向图G里顶点间能互相到达的子图。而如果一个强连通分量已经没有被其它强通分量完全包含的话,那么这个强连通分量就是极大强连通分量。

    【算法思想】

          用dfs遍历G中的每个顶点,通dfn[i]表示dfs时达到顶点i的时间,low[i]表示i所能直接或间接达到时间最小的顶点。(实际操作中low[i]不一定最小,但不会影响程序的最终结果)

          程序开始时,order初始化为0,在dfs遍历到v时,low[v]=dfn[v]=order++

    v入栈(这里的栈不是dfs的递归时系统弄出来的栈)扫描一遍v所能直接达到的顶点k,如果 k没有被访问过那么先dfs遍历klow[v]=min(low[v],low[k]);如果k在栈里,那么low[v]=min(low[v],dfn[k])(就是这里使得low[v]不一定最小,但不会影响到这里的low[v]会小于dfn[v])。扫描完所有的k以后,如果low[v]=dfn[v]时,栈里v以及v以上的顶点全部出栈,且刚刚出栈的就是一个极大强连通分量。

    【大概的证明】

     1  在栈里,当dfs遍历到v,而且已经遍历完v所能直接到达的顶点时,low[v]=dfn[v]时,v一定能到达栈里v上面的顶点:

           因为当dfs遍历到v,而且已经dfs递归调用完v所能直接到达的顶点时(假设上面没有low=dfn),这时如果发现low[v]=dfn[v],栈上面的顶点一定是刚才从顶点v递归调用时进栈的,所以v一定能够到达那些顶点。

     

    2 .dfs遍历时,如果已经遍历完v所能直接到达的顶点而low[v]=dfn[v],我们知道v一定能到达栈里v上面的顶点,这些顶点的low一定小于 自己的dfn,不然就会出栈了,也不会小于dfn[v],不然low [v]一定小于dfn[v],所以栈里v以其v以上的顶点组成的子图是一个强连通分量,如果它不是极大强连通分量的话low[v]也一定小于dfn[v](这里不再详细说),所以栈里v以其v以上的顶点组成的子图是一个极大强连通分量。

    【时间复杂度】

         因为所有的点都刚好进过一次栈,所有的边都访问的过一次,所以时间复杂度为On+m

    代码如下:

      1 // 强连通分量缩点
      2 #include <iostream>
      3 #include <cstring>
      4 #include <cstdio>
      5 #include <stack>
      6 
      7 using namespace std;
      8 
      9 const int MAX = 105;
     10 int map[MAX][MAX];
     11 int low[MAX], DFN[MAX], IN[MAX], OUT[MAX], instack[MAX], t[MAX];
     12 int n, order, res, ans;
     13 stack<int> S;
     14 
     15 void init()
     16 {
     17     memset(map, 0, sizeof(map));
     18     memset(low, 0, sizeof(low));
     19     memset(DFN, 0, sizeof(DFN));
     20     memset(IN, 0, sizeof(IN));
     21     memset(OUT, 0, sizeof(OUT));
     22     memset(instack, 0, sizeof(instack));
     23     memset(t, 0, sizeof(t));
     24     while(!S.empty())
     25        S.pop();
     26     res = 0;
     27     order = 0;
     28 }
     29 
     30 int min(int x, int y)
     31 {
     32     return x < y ? x : y;
     33 }
     34 
     35 void tr(int u)
     36 {
     37     int v;
     38     DFN[u] = low[u] = ++order;
     39     instack[u] = 1;
     40     S.push(u);
     41     for (int i = 1; i <= n; i++)
     42     {
     43         if(map[u][i])
     44         {
     45             if(!DFN[i])
     46             {
     47                 tr(i);
     48                 low[u] = min(low[u], low[i]);
     49             }
     50             else if(instack[i])
     51                 low[u] = min(low[u], DFN[i]);
     52         }
     53     }
     54     if(DFN[u] == low[u])
     55     {
     56         ++res;    // res 代表强连通分量的个数
     57         do
     58         {
     59             v=S.top();
     60             S.pop();
     61             instack[v] = 0;
     62             t[v] = res;
     63         }while(v != u);
     64     }
     65 }
     66 
     67 void tarjan()
     68 {
     69     for (int i = 1; i <= n; i++)
     70        if(!DFN[i])
     71            tr(i);
     72 }
     73 
     74 void solve()
     75 {
     76     for (int i = 1;i <= n; i++)
     77     {
     78         for (int j = 1;j <= n; j++)
     79             if(map[i][j])  // 统计每个强连通分量缩点的入度和出度
     80             {
     81                 ++IN[t[i]];
     82                 ++OUT[t[j]];
     83             }
     84     }
     85     int xx, yy;
     86     xx = yy = 0;
     87     for(int i = 1; i <= res; i++)
     88     {
     89         if(IN[i]==0)
     90            xx++;
     91         else if(OUT[i]==0)
     92            yy++;
     93     }
     94     ans = xx > yy ? xx : yy; // 结果为缩点后的有向图中出度为0或者入度为0中的大者
     95 }
     96 
     97 int main()
     98 {
     99     int T, x;
    100     scanf("%d", &T);
    101     while (T--)
    102     {
    103         init();
    104         scanf("%d",&n);
    105         for (int i = 1; i <= n; i++)
    106         {
    107             while (scanf("%d", &x), x)
    108                 map[i][x] = 1;
    109         }
    110         tarjan();
    111         solve();
    112         if(res == 1)
    113            printf("0\n");
    114         else
    115            printf("%d\n", ans);
    116     }
    117     return 0;
    118 }

    注:部分用到强连通分量的题目总结:

    POJ 2186 Popular Cows (基础)

    POJ 2553 The Bottom of a Graph (alpc OJ 1274)(基础)

    POJ 1236 Network of Schools (基础)

    2010中南赛 light sources(alpc OJ) (基础)

    POJ 2762 Going from u to v or from v to u? (中等,弱连通分量 )

    POJ 3160 Father Christmas flymouse(难,DP题)

    POJ 1904 King‘s Quest (难,推荐,非缩点,匹配思想与强连通分量的转化)

  • 相关阅读:
    linux字符设备文件的打开操作
    Linux用ps命令查找进程PID再用kill命令终止进程的方法
    Linux内核锁与中断处理
    写给大数据开发初学者的话
    zabbix监控系统客户端安装
    详解zabbix安装部署(Server端篇)
    Keepalived+Nginx架构整理版
    Nginx + Tomcat 动静分离实现负载均衡
    五个常用的Linux监控脚本代码
    16个Linux服务器监控命令
  • 原文地址:https://www.cnblogs.com/dongsheng/p/3019626.html
Copyright © 2020-2023  润新知