题意:
N个学校有网络相连 每个学校维护着一个学校列表,它向学校列表中的学校发布软件;(单向发布);
任务A: 为使每个学校都能通过网络收到软件,至少需要准备多少份软件拷贝
任务B:想确保给任意一个学校发布一个软件。该软件能发布到网络中的每个学校。为达到这个目标,必须在列表中添加新的成员,计算需要添加新成员的最小数目。
思路:
将网络中的强连通分量看做一个点,即缩点。得到一个有向无环图,如果图中某个点出发有多条路径到达顶点V,就要缩边,并要将中间顶点去掉。得到的图为一个森林。
想在森林中任意一个点出发可以遍历他的子树,因此这个点就是森林所以点的公共祖先,即任务A所求点,这样的点的特征为入度为0;
任务B的要求是添加最少边,使图完全连通,现在转为一个森林完全连通。很显然只要将一个树的叶子结点轮流连接到相邻树形结构的公共祖先结点,具体连接数量视总祖先结点和叶子结点的较大值。
#include<stdio.h>
#include<string.h>
#include<queue>
#include<vector>
#include<stack>
using namespace std;
const int maxn = 105;
const int inf = 1<<30;
int n;
int dfs_clock,scc_cnt,top;
int dfn[maxn],low[maxn],sccno[maxn],Stack[maxn];
int head[maxn],pos;
bool mark[maxn];
int in[maxn],out[maxn];
struct Node
{
int to,next;
}node[maxn*maxn];
void add( int u,int v )
{
node[pos].to = v; node[pos].next = head[u];
head[u] = pos;
pos ++;
}
void dfs( int u )
{
Stack[++top] = u;
low[u] = dfn[u] = ++dfs_clock;
for( int i = head[u]; i != -1; i = node[i].next ){
Node &v = node[i];
if( !dfn[v.to] ){
dfs(v.to);
low[u] = low[u] <= low[v.to]?low[u]:low[v.to];
}
else if( !sccno[v.to] ){
low[u] = low[u] <= dfn[v.to]?low[u]:dfn[v.to];
}
}
if( dfn[u] == low[u] ){
scc_cnt ++;
for(;;){
int x = Stack[top--];
sccno[x] = scc_cnt;
if( x == u )
break;
}
}
}
void tarjan()
{
dfs_clock = scc_cnt = top = 0;
memset( dfn,0,sizeof(dfn) );
memset( low,0,sizeof(low) );
memset( sccno,0,sizeof(sccno) );
for( int i = 1; i <= n; i ++ ){
if( !dfn[i] )
dfs( i );
}
}
int main()
{
//freopen("data.txt","r",stdin);
int v;
scanf("%d",&n);
for( int i = 1; i <= n; i ++ ){
head[i] = -1;
while( scanf("%d",&v) != EOF , v ){
add(i,v);
}
}
tarjan(); //tarjian算法求强连通分量
memset( in,0,sizeof(in) );
memset( out,0,sizeof(out) );
for( int i = 1; i <= n; i ++ ){ //计算缩点后的定点入度与出度
for( int j = head[i]; j != -1; j = node[j].next ){
if( sccno[i] != sccno[node[j].to] ){ //判断是否属于不相同的块
in[sccno[node[j].to]] ++; out[sccno[i]] ++;
}
}
}
int A = 0,B = 0;
for( int i = 1; i <= scc_cnt; i ++ ){ //统计入度为0和出度为0的顶点个数 既任务A B 的解
if( !in[i] ) A ++;
if( !out[i] ) B ++;
}
B = A > B?A:B; //需要相连的边数量视总的祖先结点和叶子结点的较大值
if( scc_cnt == 1 ) B = 0; //如果缩点完图中只有一个强连通分量 不需要将出度与入度为0的点相连
printf("%d\n%d\n",A,B);
return 0;
}