题意:有多个珠子和多条线,每条线上可以串两个珠子,珠子的位置能够重叠,问能否把珠子串在同一条竖直直线上
输入描述:第一行有两个整数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;
}