题目链接:https://www.luogu.com.cn/problem/P2746
任务A:求最少的接受软件的学校数目,能使所有学校都能用到软件。
任务B:求最少扩展几个学校,能使从任意学校发软件可以使所有学校用到。
我们抽象成图,任务A就变成选择最少几个点能遍历全图,任务B就变成加几条边,能使整个图变成强连通分量。
我们可以知道,任意一个强连通分量里的点是互相可达的,所以我们先用tarjan缩点,把整个图简化成点不使互相可达的。
现在,我们再看任务A,我们要选择几个点,或者要选择哪几个点?我们从出度入度的角度考虑,如果一个点入度为0,那么其它点到达不了它,它就必须被选择,如果一个点入度不为0,那么肯定有学校下发软件给它,它就不用被选择,所以我们只要选入度为0的点即可。任务A的答案就是图上入度为0的点的数量了。
我们再看任务B,我们要加几条边呢,或者我们要加哪几条边呢。我们再从入度出度的角度考虑,一个点能被其它点传送信息,它的入度就必须不是0,如果一个点能传送信息给其它点,那么它的出度就必须不是0,当我们加一条边时,一个点的入度+1,一个点的出度+1,我们最少要加**max(入度为0点的数量,出度为0的点的数量)**才能让所有点能被传递信息,并且能传递信息给其他点,这样就是尽可能构成一个环。任务B的答案就就是max(入度为0点的数量,出度为0的点的数量),当然只有一个点时除外,因为它不需要被别人传递信息或者传递信息给别人,这个特判一下,答案是0。
#include <bits/stdc++.h>
using namespace std;
const int maxn=102;
struct node
{
int to,next;
}p[maxn*maxn];
int dfn[maxn],low[maxn];
int col[maxn],fa[maxn];
int head[maxn];
int cnt,dfn_num,col_id;
void add(int x,int y)
{
p[++cnt].next=head[x];
p[cnt].to=y;
head[x]=cnt;
}
int stc[maxn];//模拟栈
bool vis[maxn];//标记是否在栈中
int top;
void tarjan(int u)
{
vis[u]=1;
dfn[u]=low[u]=++dfn_num;
stc[++top]=u;
for(int i=head[u];i;i=p[i].next)
{
int v=p[i].to;
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])
{
vis[u]=false;
col[u]=++col_id;
while(stc[top]!=u)
{
vis[stc[top]]=false;
col[stc[top--]]=col_id;
}
top--;
}
}
int n;
int indeg[maxn],outdeg[maxn];//入度与出度
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int y;
while(~scanf("%d",&y)&&y)
{
add(i,y);
}
}
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i);
if(col_id==1)//只有一个强连通分量
{
puts("1");
puts("0");
return 0;
}
for(int i=1;i<=n;i++)
for(int j=head[i];j;j=p[j].next)
{
int v=p[j].to;
if(col[i]!=col[v])
indeg[col[v]]++,outdeg[col[i]]++;
}
int a=0,b=0;//入度为0的数量与出度为0的数量
for(int i=1;i<=col_id;i++)
{
if(!indeg[i])
a++;
if(!outdeg[i])
b++;
}
printf("%d\n",a);
printf("%d\n",max(a,b));
return 0;
}