POJ 1182 食物链 (并查集解法)(详细注释)
食物鏈
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 78510 Accepted: 23396
Description
動物王國中有三類動物A,B,C,這三類動物的食物鏈構(gòu)成了有趣的環(huán)形。A吃B, B吃C,C吃A。
現(xiàn)有N個動物,以1-N編號。每個動物都是A,B,C中的一種,但是我們并不知道它到底是哪一種。
有人用兩種說法對這N個動物所構(gòu)成的食物鏈關(guān)系進(jìn)行描述:
第一種說法是”1 X Y”,表示X和Y是同類。
第二種說法是”2 X Y”,表示X吃Y。
此人對N個動物,用上述兩種說法,一句接一句地說出K句話,這K句話有的是真的,有的是假的。當(dāng)一句話滿足下列三條之一時,這句話就是假話,否則就是真話。
1) 當(dāng)前的話與前面的某些真的話沖突,就是假話;
2) 當(dāng)前的話中X或Y比N大,就是假話;
3) 當(dāng)前的話表示X吃X,就是假話。
你的任務(wù)是根據(jù)給定的N(1 <= N <= 50,000)和K句話(0 <= K <= 100,000),輸出假話的總數(shù)。
Input
第一行是兩個整數(shù)N和K,以一個空格分隔。
以下K行每行是三個正整數(shù) D,X,Y,兩數(shù)之間用一個空格隔開,其中D表示說法的種類。
若D=1,則表示X和Y是同類。
若D=2,則表示X吃Y。
Output
只有一個整數(shù),表示假話的數(shù)目。
Sample Input
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
Sample Output
3
這題一看就是并查集,但是要注意的一點(diǎn)是第一句話:
動物王國中有三類動物A,B,C,這三類動物的食物鏈構(gòu)成了有趣的環(huán)形。A吃B, B吃C,C吃A。
意思就是所有的種類只有A,B,C三種,只有三個關(guān)系A(chǔ)吃B, B吃C,C吃A。
這點(diǎn)看清楚就好做了。
思路:我們創(chuàng)建3個分組i-A,i-B,i-C。
如果x和y是同類,正確則合并x-A和y-A、x-B和y-B、x-C和y-C。
當(dāng)存在x吃y或者y吃x時不正確。
如果x吃y,正確則合并x-A和y-B、x-B和y-C、x-C和y-A。
當(dāng)存在x和y是同類或者y吃x時不正確。
代碼:
#include <iostream> #include <stdio.h> using namespace std; typedef long long ll; #define MAX_N 50010 #define MAX_K 100010//輸入 int N,K; //類型+X+Y int T[MAX_K],X[MAX_K],Y[MAX_K]; int par[3*MAX_N]; //par[i]表示i節(jié)點(diǎn)的父節(jié)點(diǎn) int rank[3*MAX_N]; // 樹的高度 //初始化n個元素 void init(int n){for(int i = 0;i < n; i++){par[i] = i;rank[i] = 0;} } //查詢包含x節(jié)點(diǎn)的樹的根 int find(int x){if(par[x] == x) return x;else return par[x] = find(par[x]); }//合并 x和y所屬的集合 void unite(int x,int y){x = find(x);y = find(y);if(x == y) return;if(rank[x] < rank[y]){par[x] = y; }else{par[y] = x;if(rank[x] == rank[y]) rank[x]++;} }//判斷x和y是否屬于同一個集合 bool same(int x,int y){return find(x) == find(y); }void solve(){//初始化3倍的點(diǎn),0~(n-1)屬于A類,n~(2n-1)屬于B類,2n~(3n-1)屬于C類 init(N*3);int ans = 0;for(int i = 0;i < K; i++){int t = T[i];int x = X[i] - 1,y = Y[i] - 1;if(x < 0|| N <= x || y < 0 || N <= y){ans++;continue;}//分析x和y是同類是否正確 if(t == 1){//如果x吃y || y吃x就不正確 if(same(x,y+N) || same(x,y+2*N)){ans++;}else{//x和y是同類 正確 unite(x, y); //如果x屬于A,y就屬于A unite(x+N, y+N); //如果x屬于B,y就屬于B unite(x+N*2, y+N*2); //如果x屬于C,y就屬于C}}else{//分析x吃y是否正確 //如果x和y是同類 || y吃x就不正確 if(same(x,y) || same(x,y+2*N)){ans++;}else{unite(x,y+N); //如果x屬于A,那么y就屬于B unite(x+N,y+2*N); //如果x屬于B,那么y就屬于C unite(x+2*N,y); //如果x屬于C,那么y就屬于A}}} cout << ans << endl; }int main(){cin >> N >> K;for(int i = 0;i < K; i++){scanf("%d%d%d",&T[i],&X[i],&Y[i]);}solve();return 0; }總結(jié)
以上是生活随笔為你收集整理的POJ 1182 食物链 (并查集解法)(详细注释)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何在Windows下安装Linux子系
- 下一篇: 【算法】Kruskal算法(解决最小生成