POJ-1236 Network of Schools 强连通+缩点

题意:

       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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值