题意:网络中的一学校可以将软件发送给其他一些学校,能够发送给谁取决于他们各自维护的一个清单。将学校看成一个节点,给出每个学校的维护清单,问至少需要复制几次软件,使毎个学校都能够得到该软件,在清单中至少添加几项,可使软件至少复制一次,所有学校都可以得到。
思路:
1、用Tarjan算法求出强连通分分量。2、缩点重新构图。3、分别求节点的出度和入度。
第一个问题就是入度为零的个数,第二问题就是出度和入度中的较大者。
#include<iostream>
#include<memory.h>
#include<stdio.h>
using namespace std;
struct edge
{
int t;
edge *next;
}*V[101],*s;
int DFN[101],LOW[101];//DFN[u]为节点u搜索的次序编号,
//low[u]为节点u的子树能够追溯到的最早的栈中节点的次序号
int Stop,Bcnt,Dindex;
bool instack[101];
int Stap[101];
int n,m;
int Belong[101];
int in[101];//入度
int out[101];//出度
void insert(int u,int v,edge *k[])
{
edge *temp = new edge;
temp -> t = v;
temp -> next = k[u];
k[u] = temp;
}
void tarjan(int i)
{
int j;
DFN[i] = LOW[i] = ++Dindex;
Stap[++Stop] = i;
instack[i] = true;
for(edge *e = V[i]; e ; e = e->next)
{
j = e->t;
if(!DFN[j])
{
tarjan(j);
if(LOW[i] > LOW[j])
LOW[i] = LOW[j];
}
else if(instack[j]&&DFN[j] < LOW[i])
LOW[i] = DFN[j];
}
if(DFN[i] == LOW[i])
{
++Bcnt;
do
{
j = Stap[Stop--];
instack[j] = false;
Belong[j] = Bcnt;
}
while(j != i);
}
}
void find()
{
Stop = Bcnt = Dindex;
memset(DFN , 0 ,sizeof(DFN));
for(int i = 0 ; i < n ; ++i)
{
if(!DFN[i])
tarjan(i);
}
}
int main()
{
cin>>n;
for(int i = 0 ; i < n ; ++i)
{
while(1)
{
int a;
if(scanf("%d",&a)&&!a)
break;
insert(i , a - 1 , V);
}
}
find();
memset(in , 0 , sizeof(in));
memset(out, 0 , sizeof(out));
int sum = 0;
int sum1= 0;
for(int i = 0 ; i < n ; ++i)
{
s = V[i];
while(s)
{
int j = s ->t;
if(Belong[j] != Belong[i])
{
out[Belong[i]]++;
in[Belong[j]]++;
}
s = s->next;
}
}
for(int i = 1; i <= Bcnt; ++i)
{
if(in[i] == 0)
sum++;
if(out[i] == 0)
sum1++;
}
if(Bcnt == 1)
sum1 = 0;
else if(sum > sum1)
sum1 = sum;
printf("%d\n%d\n",sum,sum1);
return 0;
}