vijos 1540 月亮之眼 并查集

题目:

描述

吉儿是一家古董店的老板娘,由于她经营有道,小店开得红红火火。昨天,吉儿无意之中得到了散落民间几百年的珍宝—月亮之眼。吉儿深知“月亮之眼”价值连城:它是由许多珍珠相连而成的,工匠们用金线连接珍珠,每根金线连接两个珍珠;同时又对每根金线染上两种颜色,一半染成银白色,一半染成黛黑色。由于吉儿自小熟读古籍,所以还晓得“月亮之眼”的神秘传说:“月亮之眼”原是一个古代寺庙的宝物,原本是挂在佛堂的一根顶梁柱上的,整个宝物垂直悬挂,所有珍珠排成一线,且都镶嵌在柱子里,而每一根金线又都是绷紧的,并且金线的银白色一端始终在黛黑色一端的上方;然而,在一个月圆之夜,“月亮之眼”突然从柱里飞出,掉落下来,宝物本身完好无损,只是僧侣们再也无法以原样把“月亮之眼”嵌入柱子中了。吉儿望着这个神秘的宝物,回忆着童年读到的传说,顿时萌发出恢复“月亮之眼”的冲动,但是摆弄了几天依旧没有成功。

现在,要麻烦您来帮助吉儿完成这项使命。

您要设计一个程序,对于给定的“月亮之眼”进行周密分析,然后给出这串宝物几百年前嵌在佛堂顶梁柱上的排列模样。给定的“月亮之眼”有N个珍珠和P根金线,所有珍珠按一定顺序有了一个序号:1、2…、N。

格式

输入格式

输入数据包含一个“月亮之眼”的特征描述:
文件第一行有两个整数N和P,其中N表示宝物中的珍珠个数,P表示宝物中的金线根数;
以下P行描述珍珠连接情况:
文件第I+1行有三个整数,Ri1,Ri2,Li。其中Ri1表示第I根金线的银白色一端连接的珍珠序号;Ri2表示第I根金线的黛黑色一端连接的珍珠序号;Li表示第I根金线的长度。

输出格式

由于珍珠尺寸很小,所以几个珍珠可以同时镶嵌在一个位置上。

您的输出数据描述的是“月亮之眼”各个珍珠在顶梁柱上的位置,输出文件共N行:
第I行,一个整数S,它表示标号为I的珍珠在顶梁柱上距离最高位置珍珠的距离。

注意:若无解则输出仅一行,包含一个整数“-1”。

样例1

样例输入1

9 9
1 2 3
2 3 5
2 7 1
4 5 4
5 6 1
5 9 1
6 7 1
7 8 3
9 8 4

样例输出1

2
5
10
0
4
5
6
9
5

限制

1s

提示

N,P<=500

来源

Balkan OI 1998


思路:

并查集,连接两个珠子就把它们所在的子树合并。fa[]存储祖先节点,d[]存储到祖先节点的距离。


关于判断是否可行:连接两个节点时,如果两个节点x、y在同一棵树上且 d[x]+z!=d[y](z为线的长度),则不可行。因为如果d[x]+z<d[y] 线不够长,d[x]+z>d[y] 时线绷不紧。


关于合并两点:两节点有同一个根节点就不用再连接了(只需判断是否能连接而不需要真的合并)。若连接根节点不同的两节点x、y(x在上),按照并查集的做法就该连接此两点的根节点。而连接根节点是有几种情况:

1、rootx比rooty高

此时,应将rooty连在rootx上且线长为d[x]-d[y]+z。

2、rooty比rootx高

在这种情况下,应将rootx连接在rooty上且线长为d[y]-d[x]-z。

综上:

if(d[x]-d[y]+z>=0) {	 
	fa[rooty]=rootx;
	d[rooty]=d[x]-d[y]+z;
}
if(d[x]-d[y]+z<0) {
	fa[rootx]=rooty;
	d[rootx]=-(d[x]-d[y]+z);
}

因为只能低的节点指向高的节点,所以不能写abs(d[x]-d[y]+z)。


关于find函数中的更新方法:

int find(int x) {
	if(fa[x]==0) return x;
	int y=find(fa[x]);
	d[x]+=d[fa[x]];
	return fa[x]=y;
}

关于输出:

注意,输出前要把所有的点都find一下。


代码:

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;

int n,m;
int d[510]= {0},fa[510]= {0};

int find(int x) {
	if(fa[x]==0) return x;
	int y=find(fa[x]);
	d[x]+=d[fa[x]];
	return fa[x]=y;
}

int main() {

	scanf("%d%d",&n,&m);
	for(int i=1; i<=m; i++) {
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		int rootx=find(x),rooty=find(y);
		if(rootx!=rooty) {
			if(d[x]-d[y]+z>=0) {	//要分类讨论 
				fa[rooty]=rootx;
				d[rooty]=d[x]-d[y]+z;
			}
			if(d[x]-d[y]+z<0) {
				fa[rootx]=rooty;
				d[rootx]=-(d[x]-d[y]+z);
			}
		} else {
			if(d[y]!=d[x]+z) {
				printf("-1");
				return 0;
			}
		}
	}

	for(int i=1; i<=n; i++) {
		find(i);
		printf("%d\n",d[i]);
	}

	return 0;
}



©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页