[文章目录]
Description
你要写一个程序计算,为了让网络中所有的学校都用上新软件,必须接受新软件副本的最少学校数目(子任务 A)。我们想要确定通过给任意一个学校发送新软件,这个软件就会分发到网络中的所有学校。为了完成这个任务,我们可能必须扩展接收学校列表,使其加入新成员。计算最少需要增加几个扩展,使得不论我们给哪个学校发送新软件,它都会到达其余所有的学校(子任务 B)。一个扩展就是在一个学校的接收学校列表中引入一个新成员注意如果 B 在 A 学校的分发列表中,那么 A 不必也在 B 学校的列表中。 (2 <= N <= 100)
显然是一道tarjan的题,首先缩点,然后对于任务A,我们只需给入度为0的强连通分量每人一个就行了(注意,如果只有一个强连通分量,那么也得给一个)
对于任务B,只需要让每个入度为零的强连通分量入读不为0,每个出度为0的强连通分量出度不为零就行了(明显可以双管齐下么,把出度为零的强连通分量连到入读为零的上面,如果没有,就没办法了),所以就是max(rd=0,cd=0);如果只有一个强连通分量的话也成立。
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
#include <iostream>
using namespace std;
int n,x;
bool map[110][110],inz[110];
int cd[110],rd[110];
int z[110],deep[110],low[110],top,cnt,belong[110],ans,t;
int fnacd,fnard;
void tarjan(int x)
{
z[++top]=x;
inz[x]=1;
deep[x]=low[x]=++cnt;
for(int i=1;i<=n;i++)
{
if(map[x][i])
{
if(!deep[i])
{
tarjan(i);
if(low[i]<low[x])
low[x]=low[i];
}
else if(inz[i])
{
if(deep[i]<low[x])
low[x]=deep[i];
}
}
}
if(deep[x]==low[x])
{
ans++;
do
{
t=z[top--];
inz[t]=0;
belong[t]=ans;
}while(t!=x);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
while(1)
{
scanf("%d",&x);
if(x==0) break;
else map[i][x]=1;
}
}
for(int i=1;i<=n;i++)
{
if(!deep[i])
tarjan(i);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(map[i][j]&&belong[i]!=belong[j])
{
cd[belong[i]]++;
rd[belong[j]]++;
}
}
}
for(int i=1;i<=ans;i++)
{
if(!cd[i])
fnacd++;
if(!rd[i])
fnard++;
}
if(ans==1)
printf("1\n0");
else
printf("%d\n%d",fnard,max(fnard,fnacd));
return 0;
}