Network of Schools POJ1236(tarjan缩点+强连通分量模板)

Network of Schools
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 19564   Accepted: 7705

Description

A number of schools are connected to a computer network. Agreements have been developed among those schools: each school maintains a list of schools to which it distributes software (the “receiving schools”). Note that if B is in the distribution list of school A, then A does not necessarily appear in the list of school B 
You are to write a program that computes the minimal number of schools that must receive a copy of the new software in order for the software to reach all schools in the network according to the agreement (Subtask A). As a further task, we want to ensure that by sending the copy of new software to an arbitrary school, this software will reach all schools in the network. To achieve this goal we may have to extend the lists of receivers by new members. Compute the minimal number of extensions that have to be made so that whatever school we send the new software to, it will reach all other schools (Subtask B). One extension means introducing one new member into the list of receivers of one school. 

Input

The first line contains an integer N: the number of schools in the network (2 <= N <= 100). The schools are identified by the first N positive integers. Each of the next N lines describes a list of receivers. The line i+1 contains the identifiers of the receivers of school i. Each list ends with a 0. An empty list contains a 0 alone in the line.

Output

Your program should write two lines to the standard output. The first line should contain one positive integer: the solution of subtask A. The second line should contain the solution of subtask B.

Sample Input

5
2 4 3 0
4 5 0
0
0
1 0

Sample Output

1
2

Source

题目大意:

一些学校连成了网络, 在学校之间存在某个协议:每个学校都维护一张传送表,表明他们要负责将收到的软件传送到表中的所有学校。如果A在B的表中,那么B不一定在A的表中。

    现在的任务就是,给出所有学校及他们维护的表,问1、如果所有学校都要被传送到,那么需要几份软件备份;2、如果只用一份软件备份,那么需要添加几条边?

解题思路:对于第一个问题来说,需要算出来一共有多少个强连通分量,然后缩点形成一个图,寻找一共有几个入度为0的点,然后对于第二问来说,需要做的是只要算出入度为0和出度为0的点的个数最大值即可

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include<stack>
#include <ctime>
using namespace std;
typedef long long LL;

int check[105],low[105];
vector<int> tu[105];
int index,instack[105],ans[105],a[105],fllow[105],sum;
stack<int> st;
int inn[105],outt[105];
//check[u]数组表示节点u搜索的次序编号时间戳。low[u]为u能追溯到的最早的时间戳
void tarjan(int x)
{
	check[x]=low[x]=++index;//初始化搜索到的节点x
	st.push(x);//将x点放在求强连通分量的栈中
	instack[x]=1;//x点是否在栈中
	for(int i = 0;i<tu[x].size();i++)
	{
		int k=tu[x][i];
		if(!check[k])
		{
			tarjan(k);
			low[x]=min(low[x],low[k]);//比较当前点x能够到达的最早时间戳,与他的子树中的点的最早时间戳比较
		}
		else
		{
			if(instack[k])//如果x为根的子树中有K在栈中
			{
				low[x]=min(low[x],check[k]);//比较k的时间戳大小
			}
		}
	}
	if(check[x]==low[x])//x点为根节点
	{
		sum++;
		int k=0;
		while(k!=x)//直到符合条件前,这些点都是强连通分量
		{
			k=st.top();
			st.pop();
			fllow[k]=sum;//将这些点分组进行标记
			instack[k]=0;
			ans[sum]=x;
		}
	}
}
int main()
{
	int i,n,x;
	cin>>n;
	for(i=1;i<=n;i++)
	{
		for(;;)
		{
			cin>>x;
			if(x==0)
				break;
			tu[i].push_back(x);
			a[x]++;//
		}
	}
	int cnt=0;
	index=0;
	for(i=1;i<=n;i++)
		if(!check[i])//存在多个图
			tarjan(i);
	int t1,t2;
	t1=t2=0;
	int j;
	for(i=1;i<=n;i++)
	{
		for(j=0;j<tu[i].size();j++)
		{
			int k=tu[i][j];
			if(fllow[k]!=fllow[i])//
			{
				inn[fllow[k]]++;
				outt[fllow[i]]++;
			}
		}
	}
	for(i=1;i<=sum;i++)
	{
		if(inn[i]==0)
		{
			t1++;
		}
		if(outt[i]==0)
		{
			t2++;
		}
	}
	if(sum==1)
	{
		cout<<1<<endl;
		cout<<0<<endl;
		return 0;
	}
	cout<<t1<<endl;
	cout<<max(t1,t2)<<endl;
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章