狡猾的商人(帶權並查集)
題目:狡猾的商人
題目連結: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)。