vijos1540

题意:有多个珠子和多条线,每条线上可以串两个珠子,珠子的位置能够重叠,问能否把珠子串在同一条竖直直线上

输入描述:第一行有两个整数N和P(N,P<=500),其中N表示宝物中的珍珠个数,P表示宝物中的金线根数,以下P行描述珍珠连接情况:第I+1行有三个整数,Ri1,Ri2,Li。其中Ri1表示第I根金线的上端连接的珍珠序号;Ri2表示第I根金线的下端连接的珍珠序号;Li表示第I根金线的长度。

输出描述:您的输出数据描述的是“月亮之眼”各个珍珠在顶梁柱上的位置,输出文件共N行:第I行,一个整数S,它表示标号为I的珍珠在顶梁柱上距离最高位置珍珠的距离。若无解则输出仅一行,包含一个整数“-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

输出案例:

2
5
10
0
4
5
6
9
5


解题思路:刚开始写的时候,看了一下标签,是差分约束加拓扑排序,然后就默默的去百度了一下什么叫差分约束,结果看的有点晕,所以想直接用拓扑排序写,建一个邻接矩阵,然后存某点到某点的距离,然后当添加了新的珠子时DFS一下,然后发现自己DFS写的超烂,就改成了并查集的思路。我的并查集是这样的,已经确定的珠子建一个并查集,然后设这个并查集的头结点距离为0。

如下图:

当前为测试案例输入到2 7 1时已建立的并查集,其中中文表示序号,数字表示距离

当前为测试案例输入到5 9 1时又建立的并查集:

然后重头戏来了,当输入6 7 1时这两个并查集会合并,大致可以理解为下图:


记录此时一个变量minn的值为-2,以后输出结果时将每个点的距离加上abs(minn)

其中的并查集合并我采取了一个很蠢的方法,给所有的珍珠遍历一遍,判断他的头结点,如果是,改变他所代表的距离,最后在把并查集合并,另外,这道题的并查集是可以压缩路径的

此外,还要考虑-1的情况,一是珍珠的数目比所需最小线的数目多,二是最后构建出来的有多个并查集,即图有多个联通块,此时珍珠不在一个线上,三是一个珍珠的位置有二义性,即该珍珠原先已有位置,但后来重新给出的位置与之前不符,比如这组测试数据:

3 3

1 2 1

2 3 1

1 3 3


代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int maxn=500+10;

int pre[maxn];//记录点的祖先
int h[maxn];//记录点的距离
bool visit[maxn];

int Find(int x)
{
    int r=x;
    while (pre[r]!=r)
        r=pre[r];
    int i=x,j;
    while (pre[i]!=r)
    {
        j=pre[i];
        pre[i]=r;
        i=j;
    }
    return r;
}//查找祖先


int main()
{
    int n,p,i,j,minn,a,b,len,fx,fy;
    bool flag;
    while (scanf("%d%d",&n,&p)!=EOF)
    {
        flag=true;
        if (n-1>p)
        {
            flag=false;
        }//珍珠关系与线的关系不妥时

        for (i=1; i<=n; i++)
        {
            pre[i]=i;
        }
        minn=0;//用于记录最小珍珠的值,用于abs后的输出
        memset(visit,false,sizeof (visit));//用于判断点位置的二义性的辅助数组

        for (i=0; i<p; i++)
        {
            scanf("%d%d%d",&a,&b,&len);

            if (flag)
            {
                fx=Find(a);
                fy=Find(b);

                if (visit[a]&&visit[b])
                {
                    if (fx==fy)
                    {
                        if (h[a]!=h[b]-len)
                            flag=false;
                    }
                }//判断点位置二义性

                if (fx==a&&fy==b)
                {
                    pre[b]=a;
                    h[a]=0;
                    h[b]=len;
                    visit[a]=visit[b]=true;
                }//构建并查集
                else if (fx!=a&&fy==b)
                {
                    pre[b]=fx;
                    h[b]=h[a]+len;
                    visit[b]=true;
                }//构建并查集
                else if (fx==a&&fy!=b)
                {
                    visit[a]=true;
                    if (len>=h[b])
                    {
                        h[a]=h[b]-len;
                        len=len-h[b];
                        minn=min(h[a],minn);
                        pre[fy]=a;
                    }
                    else
                    {
                        h[a]=h[b]-len;
                        pre[a]=fy;
                    }
                }//构建并查集
                else
                {
                    if (h[a]+len>=h[b])
                    {

                        len=h[a]-h[b]+len;
                        for (j=1; j<=n; j++)
                        {
                            if (pre[j]==fx)
                            {
                                h[j]=h[j]-len;
                            }
                        }
                        pre[fy]=fx;
                        minn=min(h[fx],minn);
                    }
                    else
                    {
                        pre[fx]=fy;
                        len=h[b]-h[a]+len;
                        for (j=1; j<=n; j++)
                        {
                            if (pre[j]==fy)
                            {
                                h[j]=h[j]-len;
                            }
                        }
                        minn=min(pre[fy],minn);
                    }
                }//合并两个并查集
            }

        }

        int num=0;
        for (i=1; i<=n; i++)
        {
            if (pre[i]==i)
                num++;
        }//判断是否存在多个并查集
        if (num!=1||!flag)
        {
            printf("-1\n");
            continue;
        }

        minn=abs(minn);
        for (i=1; i<=n; i++)
        {
            printf("%d\n",h[i]+minn);
        }
    }
    return 1;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值