并查集 月亮之眼

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

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

您要设计一个程序,对于给定的“月亮之眼”进行周密分析,然后给出这串宝物几百年前嵌在佛堂顶梁柱上的排列模样。给定的“月亮之眼”有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
#include <iostream>
#include <cstdio>
#define maxn 505
using namespace std;
int p[maxn], v[maxn], N, M;//v表示距离根节点的距离,p表示父节点是谁
int find(int x)
{
    int t = p[x];
    if (t == x)
        return x;
    v[x] += v[t];//递推出与根节点的距离
    return p[x] = find(t);
}
int merg(int x, int y, int l)
{
    /* 对于两个不同集合的合并,由于在找集合的过程中使用了find函数,
    所以相关节点一定直接和集合树的根节点连接。设现在要连接的节点是a、b,
    它们的根节点分别是roota和rootb, a与b的距离为c, a在b的上面。
    集合的合并是集合根节点之间的连接,所以需要计算出根节点之间的距离P,
    b到roota的距离应为d[a]+c, b到rootb的距离为d[b] ,假设rootb成为roota的孩子,
    着P+d[b]=d[a]+c => P=d[a]+c-d[b].若P为负,则roota应成为rootb的孩子,
    距离为P的绝对值。 */
    int fx = find(x), fy = find(y), d;
    d = v[x] + l - v[y];
    if (fx != fy)
    {
        if (d >= 0)//判断roota、rootb那个应该是父节点
            p[fy] = fx, v[fy] = d;//rootb成为roota的孩子
        else
            p[fx] = fy, v[fx] = -d;//roota成为rootb的孩子
    }
    else if (v[x] + l != v[y])//因为绳子是紧绷的,所以应该相加相等
        return -1;
    return 1;
}
int main()
{
    int a, b, c, tmp;
    for (int i = 0; i <= maxn - 2; i++)
        v[i] = 0, p[i] = i;
    scanf("%d%d", &M, &N);
    for (int i = 0; i < N; i++)
    {
        scanf("%d%d%d", &a, &b, &c);
        if (merg(a, b, c) == -1)
        {
            printf("-1\n");
            return 0;
        }
    }
    for (int i = 1; i <= M; i++)find(i);//重新计算他们的跟?
    for (int i = 1; i <= M; i++)
        printf("%d\n", v[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值