狡猾的商人(带权并查集)

题目:狡猾的商人

题目链接: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)。

posted @ 2021-10-22 18:39  思丶君  阅读(62)  评论(0)    收藏  举报