题目大意:
一些学校连成了网络, 在学校之间存在某个协议:每个学校都维护一张传送表,表明他们要负责将收到的软件传送到表中的所有学校。如果A在B的表中,那么B不一定在A的表中。
现在的任务就是,给出所有学校及他们维护的表,问1、如果所有学校都要被传送到,那么需要几份软件备份;2、如果只用一份软件备份,那么需要添加几条边?
思路:首先找连通分量,然后看连通分量的入度为0点的总数,出度为0点的总数,那么问要向多少学校发放软件,就是入度为零的个数,这样才能保证所有点能够找到
然后第二问添加多少条边可以得到使整个图达到一个强连通分量,答案是入度为0的个数和出度为0的个数中最大的
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stack>
#include<vector>
#include<iostream>
#define MAX 110
using namespace std;
int vis[MAX];
int belong[MAX];//记录节点属于哪一个强连通分量
int dfn[MAX],low[MAX];
vector<int>gra[MAX];
stack<int>s;
int index;
int count1;//记录强连通分量的个数及编号
int out[MAX],in[MAX];
int n,m;
void tarjan(int pos)
{
int i;//在i若定义成全局变量就wa,为保险起见 若函数内部和main函数用到同样的变量名(如i)在函数内部和main函数分别定义
dfn[pos]=low[pos]=++index;
vis[pos]=1;
s.push(pos);
for(i=0;i<gra[pos].size();i++)
{
int next=gra[pos][i];
if(!dfn[next])
{
tarjan(next);
low[pos]=min(low[pos],low[next]);
}
else
if(vis[next]==1)
{
low[pos]=min(low[pos],dfn[next]);
}
}
if(dfn[pos]==low[pos])
{
count1++;
while(1)
{
int t=s.top();
belong[t]=count1;
vis[t]=0;//说明已经不在栈内
s.pop();
if(t==pos)
break;
}
}
}
int main()
{
int a;
int i,j;
int ans2;
while(scanf("%d",&n)!=EOF)
{
memset(vis,0,sizeof(vis));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(belong,0,sizeof(belong));
for(i=0;i<MAX;i++)
{
gra[i].clear();
}
while(!s.empty())
{
s.pop();
}
for(i=1;i<=n;i++)
{
while(scanf("%d",&a),a)
{
gra[i].push_back(a);
}
}
index=count1=0;//初始化不能忘
for(i=1;i<=n;i++)
{
if(!dfn[i])
{
tarjan(i);
}
}
memset(out,0,sizeof(out));
memset(in,0,sizeof(in));
for(i=1;i<=n;i++)
{
for(j=0;j<gra[i].size();j++)
{
if(belong[i]!=belong[gra[i][j]])//两个点不属于同一个强连通分量
{
out[belong[i]]++;
in[belong[gra[i][j]]]++;
}
}
}
int out1=0;
int in1=0;
for(i=1;i<=count1;i++)
{
if(out[i]==0)
{
out1++;
}
if(in[i]==0)
{
in1++;
}
}
if(count1==1)
printf("1\n0\n");
else{
ans2=max(out1,in1);
printf("%d\n",in1);
printf("%d\n",ans2);
}
}
}