题意:一些学校连成了网络, 在学校之间存在某个协议:每个学校都维护一张传送表,表明他们要负责将收到的软件传送到表中的所有学校。如果A在B的表中,那么B不一定在A的表中。现在的任务就是,给出所有学校及他们维护的表,问1、如果所有学校都要被传送到,那么需要几份软件备份;2、如果只用一份软件备份,那么需要添加几条边?
思路:先使用tarjan算法找强连通分量,并进行缩点。对于缩点后的图,求每个点的入度和出度,第1问就是图中入度为0的点的个数,而第2问则是入度为0的点的个数和出度为0的点的个数这两者中较大的那一个。(参考DAG性质:对于一个有向无环图,若想让它成为强连通图,至少需要添加max(a,b) a为入度为0的边点的数量,b为出度为0的点的数量)
代码:
#include <iostream>
#include <fstream>
#include <vector>
#include <stack>
#include <algorithm>
#define MAXN 105
using namespace std;
vector<int> node[MAXN];
stack<int> S;
int DFN[MAXN], LOW[MAXN], belong[MAXN], out[MAXN], in[MAXN];
bool vis[MAXN];
int tot;
int cnt;
void tarjan(int u)
{
int v;
DFN[u] = LOW[u] = ++tot;
S.push(u);
vis[u] = 1;
for (int i = 0; i < node[u].size(); i++)
{
v = node[u][i];
if (!DFN[v])
{
tarjan(v);
LOW[u] = min(LOW[u], LOW[v]);
}
else if(vis[v])
LOW[u] = min(LOW[u], DFN[v]);
}
if (DFN[u] == LOW[u])
{
cnt++;
do {
v = S.top();
S.pop();
vis[v] = 0;
belong[v] = cnt;
} while (v != u);
}
}
int main()
{
//fstream cin("test.txt");
int n, ans1, ans2;
ans1 = ans2 = 0;
cin >> n;
for (int i = 1; i <= n; i++)
{
int to;
while (cin >> to, to)
node[i].push_back(to);
}
for (int i = 1; i <= n; i++)
{
if (!DFN[i])
tarjan(i);
}
for (int i = 1; i <= n; i++)
for (int j = 0; j < node[i].size(); j++)
{
//计算出度
if (belong[i] != belong[node[i][j]])
{
out[belong[i]]++;
in[belong[node[i][j]]]++;
}
}
for (int i = 1; i <= cnt; i++)
{
if (in[i] == 0)
ans1++;
if (out[i]== 0)
ans2++;
}
if (cnt == 1)
cout << 1 << endl << 0 << endl;
else
{
cout << ans1 << endl;
cout << max(ans1, ans2) << endl;
}
//system("pause");
return 0;
}