校园网络
时间限制: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
来源
-
第一行输入一个整数T,表示测试数据的组数(T<10)
最先看到这道题的时候还在想是否可以用并查集来判有几个环的,后来看了网上的详解,原来是要用有向图的强连通的思想,不明白强连通的可以看看这篇文章文章
这道题主要是用tarjan + 缩点,把图中的强连通分量缩点,重新编号,这样我们求出来每个点的出度和入度,然后我们取其中的最大值,这样我们可以保证每两个点(原图中的强连通分量)可以相通。
AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
#define MAX 105
int map[MAX][MAX], DFN[MAX], low[MAX], in[MAX], out[MAX];
int flag[MAX], step[MAX];
stack<int>S;
int res, tot, M, ans;
void Init()
{
memset(map, 0, sizeof(map));
memset(DFN, 0, sizeof(DFN));
memset(low, 0, sizeof(low));
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
memset(flag, 0, sizeof(flag));
if(!S.empty())
S.pop();
tot = 0, res = 0;
}
void tarjan(int v)
{
DFN[v] = low[v] = ++tot;///初始化两个值,自己为能找到的最先访问的祖先
int u;
flag[v] = 1;
S.push(v);
for(int i = 1; i <= M; i++)
{
if(map[v][i])
{
if(!DFN[i]) ///如果该点没有访问过
{
tarjan(i);
low[v] = min(low[v], low[i]);
}
///flag[i]这个判断条件很重要,这样可以避免已经确定在其他联通图的i,因为v到i的单向边而影响到v的low
///也就是已经确定了的联通图要剔除掉,剔除的办法就是判断其还在栈中,因为已经确定了的连通图的点
///flag在下面的do while中已经设为0了(即已经从栈中剔除了)
else if(flag[i])
{
low[v] = min(low[v], low[i]);
}
}
}
if(DFN[v]==low[v])
{
++res;
do
{
u = S.top();
S.pop();
flag[u] = 0;
step[u] = res;
}while(v!=u);
}
}
void solve()
{
for(int i = 1; i <= M; i++)
{
for(int j = 1; j <= M; j++)
{
if(step[i]!=step[j]&&map[i][j]) ///在找缩点之后的点的入度和出度的时候 i和j不能再同一个强连通分量中
in[step[j]]++, out[step[i]]++;
}
}
int xx = 0, yy = 0;
for(int i = 1; i <= res; i++)
{
if(in[i]==0)
xx++;
else if(out[i]==0)
yy++;
}
ans = max(xx, yy);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
Init();
scanf("%d",&M);
for(int i = 1; i <= M; i++)
{
int x;
while(scanf("%d",&x),x)
{
map[i][x] = 1;
}
}
for(int i = 1; i <= M; i++)
{
if(!DFN[i])
{
tarjan(i);
}
}
solve();
if(ans==1)
printf("0\n");
else
printf("%d\n",ans);
}
return 0;
}