题意:
网络中有一些学校,每个学校可以分发软件给其他学校。可以向哪个分发取决于他们各自维护的一个清单。
有两个问题,1:至少要copy多少份新软件给那些学校, 才能使得每个学校都能得到。
2:要在所有的学校的清单里面至少一共增加几项才能 使得 把软件给任意一个学校,所有的学校都能收得到。
思路:
此题是一个有向图,求有向图的强连同分量+缩点。
先跑一边tarjan算法,缩点。
第一问:入度为0的点的个数即为答案。
第二问:入度或出度为0的个数的较大值为答案,因为缩点之后构成一棵树,树的叶子和根结点都必须和其他的点相连才能构成一个连通图。
#include <iostream>
using namespace std;
const int M = 105;
struct Node
{
int to;
Node *next;
};
Node *g[M];
int low[M], stack[M], ord[M], id[M];
int cnt, scnt;
bool instack[M], map[M], in[M], out[M];
void init(int n)
{
for(int i=1; i<=n; i++)
{
in[i] = true;
out[i] = true;
low[i] = 0;
ord[i] = 0;
id[i] = 0;
instack[i] = false;
map[i] = true;
}
}
void insert(int u, int v)
{
Node *tmp = new Node;
tmp -> to = v;
tmp -> next = g[u];
g[u] = tmp;
}
void Tarjan(int n, int e)
{
cnt++;
low[e] = ord[e] = cnt;
instack[e] = true;
stack[++stack[0]] = e;
Node *tmp = g[e];
while (tmp != NULL)
{
int t = tmp -> to;
if(!ord[t])
{
Tarjan(n, t);
if(low[e] > low[t])
low[e] = low[t];
}
else if(instack[t] && ord[t]<low[e])
low[e] = ord[t];
tmp = tmp -> next;
}
if(low[e] == ord[e])
{
int t;
scnt++;
do
{
t = stack[stack[0]--];
id[t] = scnt;
instack[t] = false;
}while (t != e);
}
return ;
}
void find_compentents(int n)
{
cnt = scnt = 0;
stack[0] = 0;
for(int i=1; i<=n; i++)
{
if(!ord[i])
Tarjan(n, i);
}
return ;
}
int main()
{
int n,i;
while (scanf("%d",&n)!=EOF)
{
init(n);
for( i=1; i<=n; i++)
{
while (1)
{
int a;
scanf("%d",&a);
if(a == 0)
break;
insert(i, a);
}
}
find_compentents(n);
int ansin = 0, ansout = 0;
for( i=1; i<=n; i++)
{
Node *tmp;
tmp = g[i];
while (tmp != NULL)
{
int e = tmp -> to;
if(id[i]!=id[e] && out[id[i]])
out[id[i]] = false;
if(id[i]!=id[e] && in[id[e]])
in[id[e]] = false;
tmp = tmp -> next;
}
}
for( i=1; i<=scnt; i++)
{
if(in[i])
ansin ++;
if(out[i])
ansout ++;
}
if(scnt == 1)
ansout = 0;
else
ansout = ansout>ansin ? ansout:ansin;
printf("%d\n%d\n",ansin,ansout);
}
return 0;
}