NOJ1081组织集体活动——全连通判断+最小生成树

组织集体活动

Time Limit(Common/Java):1000MS/3000MS          Memory Limit:65536KByte
Total Submit:253            Accepted:52

Description

成功的组织一次集体活动基本要求是所有人都参加,从实践来看,说服所有人需要花费较多的时间。幸运的是,研究发现,在一个集体中,两个人之间花费一定的时间进行单独交流,会同意参加集体活动。现在已知集体中两两之间对参加集体活动达成一致时需要花费的时间t,由于组织集体活动有时间限制,当t大于24小时,我们认为这两人在有限时间不能达成一致,数据不作记录。现在我们采用两两说服的方式来组织这次集体活动,请你根据统计数据,判断能否让所有人都参加,如果能,输出累计花费的最少时间(所有的两两交流所花费时间的累加和)

Input

第一行是一个正整数:测试用例数目,最多为100。之后,每个测试用例包括多行:

l       第1行给出一个整数(空格分割),表示集体人数n,2≤n≤200

l       第2行给出两个整数(空格分割),第一个整数表示对某项决定达成一致时间不大于24小时的两人组数目m,第三个整数表示集体活动最初的倡议者k,0≤m≤20100,0≤k≤n-1,集体中每个人用序号表示。

l       m行,每行三个整数(空格分割),前两个整数表示两个人,第三个整数表示上述两个人同意参加集体活动需要花费的时间t,0≤n<24。

Output

对于每个测试用例:

l       不能让所有人都参加则输出“Impossible”,否则输出累计花费的最少时间

注意:输出部分的结尾要求包含一个多余的空行。

Sample Input

2
2
1 0
0 1 2
3
1 0
0 1 2

Sample Output

2
Impossible

Source

算法与数据结构设计2009


分析:全连通判断有3种方法:之前的文章有详细记录。这里在强化一下。

有Warshall、BFS、DFS。

Warshall较简单,只要互相连通或者能够通过其他节点达到连通的,都记录为1。最后遍历全图,如果有0,说明不是全连通。需要注意的是初始化和无向图。

其他2种方法,利用了搜索的特性,比如BFS要用到队列,记录任意一个节点能够到达的节点数,如果1个节点能够到达其他所有节点,那么显然是全连通的。DFS需要递归,记录每个节点是否访问过,利用深度遍历的特性,也比较方便,但是切记DFS需要驱动:)


之后就是最小生成树,较简单,我就用了prim算法。

此题花了较多时间,分析出了解法,遇到了不少问题。比如因为是无向图,在全连通判断要很小心,另外最初的倡议者k没什么用。题意也需要时间理解~因为会有不连通的,所以一开始数组a一定要初始化为INF。INF代表一个大值表示不连通。还有INF的取值,我一开始和之前一样定义INF为10亿,这里出错。INF最后取值0x四个3f。如果是10亿数组初始化出错。但是照理说int最大值为21亿,这里有点奇怪!

//组织集体活动——tmp初始化+INF
#include<iostream>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f // need 深究
//#define INF 1000000000 // 出错

int ncase, n, m, start, ans;
int a[400][400];
bool vis[400];
int dis[400];
int tmp[400][400];

bool allArrive() // 全连通 0~n-1——
{
	memset(tmp, 0, sizeof(tmp));
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		{
			if(a[i][j] < INF)
				tmp[i][j] = tmp[j][i] = 1;
			else
				tmp[i][j] = tmp[j][i] = 0;
		}
		tmp[i][i] = 1;
	}
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		{
			if(tmp[i][j])
			{
				for(int k=0;k<n;k++)
				{
					if(tmp[k][i])
						tmp[k][j] = tmp[j][k] = 1;
				}
			}
		}
	}
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			if(!tmp[i][j])
				return false;
	return true;	
}

bool prim(int n)
{
	memset(vis, 0, sizeof(vis));
	for(int i=0;i<n;i++)
		dis[i] = INF;
	ans = 0; dis[0] = 0;
	for(int i=0;i<n;i++)
	{
		int tmp = INF, k = 0;
		for(int j=0;j<n;j++)
		{
			if(!vis[j] && dis[j] < tmp)
			{
				tmp = dis[j];
				k = j;
			}
		}
		if(tmp == INF) return false;
		vis[k] = true;
		ans += tmp;
		for(int j=0;j<n;j++)
		{
			if(!vis[j] && dis[j]>a[k][j])
				dis[j] = a[k][j];
		}
	}
	return true;
}

int main()
{
	scanf("%d",&ncase);
	while(ncase --)
	{
		int x, y, z;
		memset(a, INF, sizeof(a));
		scanf("%d%d%d",&n,&m,&start);
		for(int i=0;i<m;i++)
		{
			scanf("%d%d%d",&x,&y,&z);
			a[x][y] = z;
			a[y][x] = z;
		}
		if(allArrive() == false)
			printf("Impossible\n");
		else
		{
			prim(n);
			printf("%d\n",ans);
		}
	}
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值