狡猾的商人(带权并查集)
题目:狡猾的商人
题目链接:https://ac.nowcoder.com/acm/problem/20044
题意:商人有一个账本,账本记录为x y z,表示从x月到y月(区间左闭右闭)赚了z元,z为负数时表示亏损。问账本记录之间是否有冲突。
输入:第一行输入测试用例个数t。
接下来t个测试用例,每个测试用例第一行输入n(总共的月数)和m(记录的条数)。
接下来m行,每行三个整数x y z,表示一条账本记录。
题目保证:t < 100, n < 100, m < 1000, x <= y。
输出:若账本之间无冲突,则输出true;否则,输出false。
样例输入:
1
3 3
1 2 10
1 3 -5
3 3 -15
样例输出:
true
样例解释:第一个月到第二个月赚了10元,第三月亏了15元,第一个到第三月亏了5元。
题目分析:带权并查集。
解题步骤:对于每条账本记录,若x和y在同一个集合,则判断从x月到y月是不是赚了z元,若不是,则说明账本记录之间有冲突;若x和y不在同一个集合,则合并x和y。
需要注意的是,由于本题x和y都是闭区间,所以要把x – 1,或者y + 1,因为[x, y]之间有y – x + 1个月。
AC代码:
#include<iostream>
using namespace std;
const int N = 110;
int parent[N], val[N];
int find(int x){
if(x == parent[x]) return x;
int y = find(parent[x]);
val[x] += val[parent[x]];
return parent[x] = y;
}
void Union(int x, int y, int z){
int p1 = find(x), p2 = find(y);
parent[p1] = p2;
val[p1] = z + val[y] - val[x];
}
void solve(){
int n, m;
scanf("%d %d", &n, &m);
for(int i = 0;i <= n;i++){
parent[i] = i;
val[i] = 0;
}
int ans = 0;
while(m--){
int x, y, z;
scanf("%d %d %d", &x, &y, &z);
x--;
if(find(x) == find(y)){
if(val[x] - val[y] != z) ans = 1;
}
else Union(x, y, z);
}
if(ans == 0) printf("true\n");
else printf("false\n");
}
int main(void){
int t;
scanf("%d", &t);
while(t--) solve();
return 0;
}
时间复杂度:O(n + mlogm),路径压缩的并查集的时间复杂度接近于logn。
空间复杂度:O(n)。

浙公网安备 33010602011771号