POJ-1236-Network of Schools
http://poj.org/problem?id=1236
给一个有向图,问题1:最少从多少个点开始搜索能访问到图所有点,问题2:至少加几条边才能使从任一点搜索即能访问所有点
问题1的答案应该为入度为0的点的个数
问题2的答案即为入度为0的点和出度为0的点个数多的那个
Kosaraju_Algorithm:
step1:对原图G进行深度优先遍历,记录每个节点的离开时间。
step2:选择具有最晚离开时间的顶点,对反图GT进行遍历,删除能够遍历到的顶点,这些顶点构成一个强连通分量。
step3:如果还有顶点没有删除,继续step2,否则算法结束
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define M 150
int Max(int a,int b)
{
return a>b?a:b;
}
int map[M][M];
int vis[M],order[M],belong[M],indegree[M],outdegree[M];
int n,num,count;
void dfs(int x)
{
int i;
vis[x]=1;
for(i=1;i<=n;i++)
if(map[x][i]&&!vis[i])
dfs(i);
order[++num]=x;
}
void dfst(int x)
{
int i;
belong[x]=count; //记录结点属于哪个连通分量
vis[x]=1;
for(i=1;i<=n;i++)
if(!vis[i]&&map[i][x])
dfst(i);
}
void solve()
{
int i;
memset(vis,0,sizeof(vis));
num=count=0;
for(i=1;i<=n;i++) //第一次搜索将时间戳从小到大排序
if(!vis[i])
dfs(i);
memset(vis,0,sizeof(vis));
for(i=n;i>=1;i--) //第二次搜索从时间戳大的开始走连通分量
if(!vis[order[i]])
{
count++; //连通分量数
dfst(order[i]);
}
}
void output()
{
int i,j,inzero=0,outzero=0;
for(i=1;i<=n;i++)
indegree[i]=outdegree[i]=0;
for(i=1;i<=n;i++) //连通分量入度和出度
for(j=1;j<=n;j++)
if(map[i][j]&&belong[i]!=belong[j])
{
indegree[belong[j]]++;
outdegree[belong[i]]++;
}
for(i=1;i<=count;i++) //找入度与出度为0的点
{
if(!indegree[i])
inzero++;
if(!outdegree[i])
outzero++;
}
if(count==1)
printf("1\n0\n");
else
printf("%d\n%d\n",inzero,Max(inzero,outzero));
}
int main()
{
int i,a;
while(scanf("%d",&n)!=EOF)
{
for(i=1;i<=n;i++)
{
while(scanf("%d",&a),a)
map[i][a]=1;
}
solve();
output();
}
return 0;
}