终于把Tarjian算法看懂了。
但是遇到某个贴别题目时还是没有思路,看完题解后自己敲有有了好几处BUG,WA的我泪流满面啊,原来就是把某处的一个变量写错了
题意:
一些学校连成了网络, 在学校之间存在某个协议:每个学校都维护一张传送表,表明他们要负责将收到的软件传送到表中的所有学校。如果A在B的表中,那么B不一定在A的表中。
现在的任务就是,给出所有学校及他们维护的表,问1、如果所有学校都要被传送到,那么需要几份软件备份;2、如果只用一份软件备份,那么需要添加几条边?
分析:可以先用Tarjian算法求出强连通分量,然后进行缩点,然后重新建图,对第一问求出入度为0的点即可,对第二问,为入读为0的点数和出度为0的点数的最大值,这个结论目前还不太懂,以后要注意灵活运用啊 ,100个点用邻接矩阵够了。
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
using namespace std;
int n, cnt, tim, top;
const int maxn = 100 + 5;
int a[maxn][maxn], vis[maxn], dfn[maxn], low[maxn];
int in[maxn], out[maxn], belong[maxn], instack[maxn];
void dfs(int u)
{
vis[u] = 1;
dfn[u] = low[u] = tim;//dfn[i]表示访问该点的最早时间
tim++; //low[i]表示从节点i出发能够到达的点的开始最早的时间
instack[++top] = u;//对于访问过的节点入栈
for (int i = 1; i <= n; i++)
{
if (!a[u][i]) continue;
if (!vis[i])//如果没访问过子节点,向下搜,然后更新
{
dfs(i);
low[u] = min(low[u], low[i]);
}
else if (vis[i] == 1)low[u] = min(low[u], dfn[i]);
//如果已经访问过了,并且在栈中,说明u可达的最早节点是i
}
if (dfn[u] == low[u])//栈里u以上的就是一个强连通分量
{
int tmp;
do
{
tmp = instack[top--];
belong[tmp] = cnt;//缩点
vis[tmp] = 2;//出栈
}
while (tmp != u);
cnt++;
}
}
void Init()
{
memset(a, 0, sizeof(a));
memset(vis, 0, sizeof(vis));
memset(low, 0, sizeof(low));
memset(dfn, 0, sizeof(dfn));
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
memset(instack, 0, sizeof(instack));
tim = 1;
top = 0;
cnt = 1;
}
int main()
{
//freopen("input.txt", "r", stdin);
while (cin >> n)
{
Init();
for (int i = 1; i <= n; i++)
{
int x;
while (scanf("%d", &x) == 1 && x)
{
a[i][x] = 1;
}
}
for (int i = 1; i <= n; i++)
{
if (!vis[i])dfs(i);
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (a[i][j] && belong[i] != belong[j])
{
out[belong[i]]++;
in[belong[j]]++;
}
}
}
int t1 = 0, t2 = 0;
for (int i = 1; i < cnt; i++)
{
if (in[i] == 0)t1++;//入读为0的数量
if (out[i] == 0)t2++;//出度为0的数量
}
if (cnt == 2)
{
printf("1\n0\n");
}
else {
cout << t1 << endl;
cout << max(t1, t2) << endl;
}
}
return 0;
}