NYOJ-120 校园网络(强连通缩点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(难,推荐,非缩点,匹配思想与强连通分量的转化)

转载于:https://www.cnblogs.com/dongsheng/archive/2013/04/14/3019626.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
孪生素数是指两个素数之间的差值为2的素数对。通过筛选法可以找出给定素数范围内的所有孪生素数的组数。 在引用的代码中,使用了递归筛选法来解决孪生素数问题。该程序首先使用循环将素数的倍数标记为非素数,然后再遍历素数数组,找出相邻素数之间差值为2的素数对,并统计总数。 具体实现过程如下: 1. 定义一个数组a[N,用来标记数字是否为素数,其中N为素数范围的上限。 2. 初始化数组a,将0和1标记为非素数。 3. 输入要查询的孪生素数的个数n。 4. 循环n次,每次读入一个要查询的素数范围num。 5. 使用两层循环,外层循环从2遍历到num/2,内层循环从i的平方开始,将素数的倍数标记为非素数。 6. 再次循环遍历素数数组,找出相邻素数之间差值为2的素数对,并统计总数。 7. 输出总数。 至此,我们可以使用这个筛选法的程序来解决孪生素数问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [python用递归筛选法求N以内的孪生质数(孪生素数)](https://blog.csdn.net/weixin_39734646/article/details/110990629)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [NYOJ-26 孪生素数问题](https://blog.csdn.net/memoryofyck/article/details/52059096)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值