Codeforces 400 D.Dima and Bacteria(并查集+弗洛伊德)

题目:http://codeforces.com/contest/400/problem/D

N个点,M条边,每条边有一个权值x,x可以为0。然后把这些点分成K组,给一个序列C,共K个数,C[i]>=1,按照编号1~N,1~C[1]为第一组,C[1]+1~C[2]为第2组,以此类推。

然后问每一组的点是否到同组其他点的最短路都是0,是输出Yes,否则输出No。

对于Yes的情况,还要输出一个K*K的矩阵D,D[i][j]代表第i组点到第j组点的最短距离,不存在路径则输出-1。

对于第一个判断,用并查集可以解决。如果两个点之间的边的权值是0,就给它们连一条边。全部边读取完之后判断同一组的点的祖先是否相同即可。

在前面读取边的时候,还可以同时更新两组之间的最短路径,然后就跑一下弗洛伊德算法就OK了。

#include<cstdio>
#include<cstring>
int n, m, q;
int i, j, k;
int c[501];//记录当前组的点数
int s[501];//记录当前组第一个点的编号
int f[100001];//并查集祖先
int p[100001];//记录点所在组
int d[501][501];
int x, y, z;
//并查集查找祖先
int find(int xx){
	int yy=xx;
	for(;xx!=f[xx];xx=f[xx]);
	return f[yy]=xx;
}
int main(){
	while(~scanf("%d %d %d", &n, &m, &q)){
		for(i=1; i<=n; i++)	f[i]=i;
		s[0]=1;
		c[0]=0;
		memset(d,-1,sizeof(d));
		for(i=1; i<=q; i++){
			d[i][i]=0;
			scanf("%d", c+i);
			s[i]=s[i-1]+c[i-1];
			for(j=s[i]; j<s[i]+c[i]; j++)	p[j]=i;
		}
		while(m--){
			scanf("%d %d %d", &x, &y, &z);
			int px = find(x);
			int py = find(y);
			if(px!=py && (!z))	f[px]=py;
			//将路径信息更新到组的距离上
			x = p[x];
			y = p[y];
			if(d[x][y]==-1 || z<d[x][y]){
				d[x][y]=d[y][x]=z;
			}
		}
		//判断同组的点是否同一祖先
		bool flag=1;
		for(i=1; i<=q; i++){
			k = find(s[i]);
			for(j=s[i]+1; j<s[i]+c[i]; j++){
				if(find(j)!=k){
					flag=0;
					break;
				}
			}
			if(!flag)	break;
		}
		if(!flag)	puts("No");
		else{
			puts("Yes");
			//弗洛伊德算法
			for(k=1; k<=q; k++){
				for(i=1; i<=q; i++){
					for(j=1; j<=q; j++){
						if(d[i][k]==-1 || d[k][j]==-1)	continue;
						int tmp = d[i][k]+d[k][j];
						if(d[i][j]==-1 || tmp<d[i][j])	d[i][j]=tmp;
					}
				}
			}
			for(i=1; i<=q; i++){
				printf("%d", d[i][1]);
				for(j=2; j<=q; j++)	printf(" %d", d[i][j]);
				puts("");
			}
		}
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值