另一位大佬的链接https://www.cnblogs.com/liyinggang/p/5327055.html
以下是我看了这篇文章的不同的感悟
题意:
给出n个数字,每个数字带有一个权值(正数),给出m个数据,每输入一组数据,判断此数据是否与前面冲突,如果冲突,错误数据+1,最后输出错误数据的个数。
思路:
并查集思想,father[x]还是为父节点,但是多了一个sum[x],这个sum[x]数组代表着x到father[x]的权值,节点的父节点一定小于等于子节点(此文章要表示的意思)
1.当输入的a与b是相同的父节点是,判断节点是否冲突。
2.为不同父节点时,将他们合并(合并在下面)
find函数(路径压缩且返回父节点的父节点的…父节点)
int find(int x){
if(x != father[x]){
int temp = father[x];
father[x] = find(father[x]);
sum[x] += sum[temp];
}
return father[x];
}
例如:find(1) :father[1] = 2,father[2] = 3,father[3] = 3;从1找到2再找到3,当x=3递归返回3,进入x=2的find函数,得:sum[2] = sum[2]+sum[3],再进入1的find函数得:sum[1] = sum[1] + sum[2],本来1的父节点是2,2的父节点是3,此操作直接将1的父节点变成了3,不仅节点与节点连接,值也与相应的节点的值连接了。
样例
10 5
1 10 100
7 10 28
1 3 32
4 6 41
6 6 1
有10个数字,给出5个数据
1 10 100
sum[1~10] = 100
7 10 100
先判断此数据是否与前面所有以保存的数据冲突
这里可知不冲突,所以
sum[7~10] = 28
sum[1~6] = 100 - 28
1 3 32
与上例一样
sum[1~3] = 32
4 6 41
因为已知sum[1~3] = 32
还已知sum[7~10] = 100 - 28(sum[1-10] - sum[1-6])
所以此数据与前面的不符,ans++,直接跳过这个数据
6 6 1
不冲突 sum[6~6] = 1
所以输出1
合并代码
void unionset(int a,int b,int u,int v,int w){
if(u > v){
val[u] = val[b] - w -val[a];
father[u] = v;
}
else{
val[v] = val[a] + w - val[b];
father[v] = u;
}
}
- 代码解释(u为a的父节点,v为b的父节点)
已知10的父节点是1,和7的父节点是6,10-1的值和7-6的值都知道,他们是不同的父节点,所以进行合并操作,合并时判断u和v的大小,如果u大于v,则将u合并在v上,此时u的父节点变成了v(因为本文规定父节点总是小于等于子节点)。
所以看路线图,w + 30 + y应该等于100所以 val[u] = val[b] - w -val[a](val就是sum,这里不记得改了)
这种情况为val[v] = val[a] + w - val[b](看向量的指向)
注意!!:图片里的a没有-1,因为[a,b] = [v,b]-[u,a-1],我这样画是为了方便理解。
全部代码
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 200010;
int father[maxn];
int val[maxn];
int sign = 0;
int find(int x){
if(x != father[x]){
int temp = father[x];
father[x] = find(father[x]);
val[x] += val[temp];
}
return father[x];
}
void unionset(int a,int b,int u,int v,int w){
if(u > v){
val[u] = val[b] - w -val[a];
father[u] = v;
}
else{
val[v] = val[a] + w - val[b];
father[v] = u;
}
}
int main()
{
int n,m,a,b,w;
while(scanf("%d%d",&n,&m) != EOF){
//init
for(int i=0;i<=n+1;i++) father[i] = i;
sign = 0;
memset(val,0,sizeof(val));
while(m--){
scanf("%d%d%d",&a,&b,&w);
//a-- ro b++ 都可以通过
a--;
//b++;
int u = find(a);
int v = find(b);
if(u == v){
if(val[b] != val[a] + w) sign++;
}
else{
unionset(a,b,u,v,w);
}
}
printf("%d\n",sign);
}
return 0;
}