这道题的意思是给出一些公司,一些公司可以给他旗下的公司分配软件,第一问是至少需要分配给多少个公司软件才能使所有的公司都能得到这个软件,第二问是最少要加多少条边才能保证无论给哪个公司软件,其他的公司也都能得到。也就是给出一个无向图,求强连通分量,然后缩点,重新构图,统计这个新图的入度和出度。缩点后入度为0的点即为第一问的答案。第二问是要你加边把新图变成强连通的,也就是说让原图中入度和出度为0的点都有连通其他点的边,所以最小值就是入度为0的点和出度为0的点数目的较大者
#include <iostream>
#include <cstdio>
#include <string.h>
#include <vector>
#include <stack>
using namespace std;
int n;
#define MAXN 110 //题目中可能的最大点数
stack<int> s; //tarjan 算法中的栈
bool instack[MAXN]; //检查是否在栈中
int dfn[MAXN]; //深度优先搜索访问次序
int low[MAXN]; //能追溯到的最早的次序
int scnt=0; //有向图强连通分量个数
int index=0; //索引号
vector <int> Edge[MAXN]; //邻接表表示
vector <int> Component[MAXN]; //获得强连通分量结果
int belong[MAXN]; //记录每个点在第几号强连通分量里
int ComponentDegree[MAXN]; //记录每个强连通分量的度
int ind[MAXN];
int outd[MAXN];
void tarjan(int i)
{
int j;
dfn[i]=low[i]=index++;
instack[i]=true;
s.push(i);
for (int e=0; e<Edge[i].size(); e++)
{
j=Edge[i][e];
if (dfn[j]==-1)
{
tarjan(j);
low[i]=min(low[i],low[j]);
}
else if (instack[j])
low[i]=min(low[i],dfn[j]);
}
if (dfn[i]==low[i])
{
scnt++;
do
{
j=s.top(); s.pop();
instack[j]=false;
//Component[scnt].push_back(j);
belong[j]=scnt;
}
while (j!=i);
}
}
void solve(int n) //n是此图中点的个数,注意是0-indexed!
{
memset(instack,0,sizeof(instack));
memset(dfn,-1,sizeof(dfn));
memset(low,-1,sizeof(low));
scnt=index=0;
for(int i=1; i<=n; i++)
if(dfn[i]==-1)
tarjan(i);
}
void find()
{
for(int i=1;i<=n;i++)
{
for(int j=0;j<Edge[i].size();j++)
{
if(belong[i]!=belong[Edge[i][j]])
{
outd[belong[i]]++;
ind[belong[Edge[i][j]]]++;
}
}
}
}
int main()
{
//freopen("input.txt","r",stdin);
while(scanf("%d",&n)!=EOF)
{
memset(ind,0,sizeof(ind));
memset(outd,0,sizeof(outd));
for(int i=1;i<=n;i++)
{
Edge[i].clear();
//Component[i].clear();
int temp;
while(scanf("%d",&temp),temp)
{
if(temp==i)
continue;
Edge[i].push_back(temp);
}
}
solve(n);
find();
int ans=0,temp=0;
for(int i=1;i<=scnt;i++)
{
if(!ind[i])
ans++;
if(!outd[i])
temp++;
}
printf("%d\n",ans);
printf("%d\n",scnt?max(ans,temp):0);
}
}