poj 1236-Network of Schools:Tarjan算法实现

点击打开链接

Network of Schools
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 9214 Accepted: 3654

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


第一次写tarjan算法,感觉没有想象中的难,但是还是wa了很多次,。有一些细节没弄对,在遍历图时一共有4种点:1、未搜索过的点,2、搜索过,并且在栈中的点,2、搜索过,但已经弹出的点。4、当前搜索的点。wa了很多次是因为搞错了2类和3类的点。我之前只用了一个数组,跟普通的深搜一样,搜索过就标记一下,搜索时如果遇到标记过的点就比较low值和num值,但是这样就没法区分2类和3类点,如果这个点已经标记了,而且不再栈中,这时候是不能比较low和num的值的,以为这两个点根本不在一个连通分量里,如果更新当前节点的low值,那么low会指向一个不再本强连通分量的点,正确的做法是用两个数组标记,一个标记是否搜索过,一个标记是否在栈中,如果没搜索过就继续搜,如果搜索过,还要判断一下是否在栈中,如果在栈中,则更新当前节点的low值,否则不更新,还要注意一下如果全图就是一个强连通的,那么第二个答案是0否则,第二个答案是max(入度为0的颜色的个数,出度为0的颜色的个数)。

AC代码:

#include<stdio.h>
#include<stack>
using namespace std;
stack<int> Stack;
bool map[110][110];
int n;
bool used[110];
int num[110];
int low[110];
int count;
int time;
bool instack[110];
int color[110];
int color_num;

void dfs(int node)
{
	int i;

	low[node] = time;
	num[node] = time++;
	used[node] = 1;
	Stack.push(node);
	instack[node] = 1;
	for(i = 1; i <= n ; i++)
	{
		if(map[node][i] != 0)
		{
			if(used[i] == 1)
			{
				if(instack[i] == 1)
					low[node] = low[node] > num[i] ? num[i] : low[node];
			}
			else
			{
				dfs(i);
				low[node] = low[node] > low[i] ? low[i] : low[node];
			}
		}
	}
	int top;
	if(low[node] == num[node])
	{
		while(!Stack.empty())
		{
			top = Stack.top();
			Stack.pop();
			instack[top] = 0;
			color[top] = color_num;
			if(top == node)
			{
				color_num++;
				break;
			}
		}
	}
}
int main()
{
//	freopen("test.txt", "r", stdin);
	scanf("%d", &n);
	color_num = 1;
	int i;
	for(i = 1; i<= n; i++)
	{
		int p;
		while(scanf("%d", &p), p != 0)
		{
			map[i][p] = 1;
		}
	}
	for(i = 1; i <= n; i++)
	{
		if(used[i] == 0)
		{
			dfs(i);
		}
	}
	int income_used_color[110] = {0};//记录入度为0的颜色
	int outcome_used_color[110] = {0};//记录出度为0的颜色
	int j;
	for(i = 1; i <= n; i++)
	{
		
		for(j = 1; j <= n; j++)
		{
			if(map[i][j] == 1)
			{
				if(color[i] != color[j])
				{
					income_used_color[color[j]] = 1;
					outcome_used_color[color[i]] = 1;
				}
			}
		}
	}
	int flag_income = 0;
	int flag_outcome = 0;
	for(i = 1; i < color_num; i++)
	{
		if(income_used_color[i] == 0)
			flag_income ++;
		if(outcome_used_color[i] == 0)
			flag_outcome ++;
	}
	if(color_num == 2)
		printf("1\n0\n");
	else
		printf("%d\n%d\n", flag_income, flag_income > flag_outcome? flag_income : flag_outcome);
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

勇敢的炮灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值