食物鏈 poj-1182
題面:
動物王國中有三類動物A,B,C,這三類動物的食物鏈構成了有趣的環形。A喫B, B喫C,C喫A。
現有N個動物,以1-N編號。每個動物都是A,B,C中的一種,但是我們並不知道它到底是哪一種。
有人用兩種說法對這N個動物所構成的食物鏈關係進行描述:
第一種說法是"1 X Y",表示X和Y是同類。
第二種說法是"2 X Y",表示X喫Y。
此人對N個動物,用上述兩種說法,一句接一句地說出K句話,這K句話有的是真的,有的是假的。當一句話滿足下列三條之一時,這句話就是假話,否則就是真話。
1) 當前的話與前面的某些真的話衝突,就是假話;
2) 當前的話中X或Y比N大,就是假話;
3) 當前的話表示X喫X,就是假話。
你的任務是根據給定的N(1 <= N <= 50,000)和K句話(0 <= K <= 100,000),輸出假話的總數。
Input
第一行是兩個整數N和K,以一個空格分隔。
以下K行每行是三個正整數 D,X,Y,兩數之間用一個空格隔開,其中D表示說法的種類。
若D=1,則表示X和Y是同類。
若D=2,則表示X喫Y。
Output
只有一個整數,表示假話的數目。
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
這道題明顯是一道使用並查集算法的題目,因爲牽涉到兩個動物是否同爲一類的問題。因爲所有動物都屬於A,B,C中的其中一種。所以在的到n種動物的數據後,初始化爲3 * n個節點。這樣做的目的主要是用來表示捕食關係。
若給的兩個動物屬於同一類,則執行將每一個n範圍的節點中代表的兩個動物放在一個集合中。
用計算機語言來表達就是:
if(a == 1) { //x和y屬於同一類的信息
if(same(b, c + m) || same(b, c + 2*m)) { //判斷b和c在之前的情況中,是否已構成捕食與被捕食的關係
ans++;
} else { //如果沒有捕食關係,則在3個區間中把b和c放在一起
unite(b, c);
unite(b + m, c + m);
unite(b + m*2, c + m*2);
}
}
若給的兩個動物是捕食關係,則執行將第一個n範圍中的第一個動物捕食第二個n範圍中的第二個動物,第二個n範圍彙總的第一個動物捕食第三個n範圍中的第二個動物,第三個n範圍中的第一個動物捕食第一個範圍中的第二個動物。
用計算機語言表達就是:
if(t == 2) { //x喫y的信息
if(same(b, c) || same(b, c + 2*m)) { //判斷b和c是否爲同一類或者已經構成c喫b的捕食關係,若是則爲錯ans+1
ans++;
} else {
unite(b, c + m);
unite(b + m, c + 2*m);
unite(b + 2*m, c);
}
}
AC代碼:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int SIZE = 1e6+5;
int n, k;
int par[SIZE], Rank[SIZE];
void init(int x) {
for(int i = 0; i < x; i++) {
par[i] = i;
Rank[i] = 0;
}
}
int Find(int x) {
if(par[x] == x) {
return x;
} else {
return par[x] = Find(par[x]);
}
}
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]++;
}
}
bool same(int x, int y) {
return Find(x) == Find(y);
}
int main() {
int m, n, a, b, c, ans;
scanf("%d%d", &m, &n);
init(m*3); //初始化:準備m*3個節點表示n個元素
ans = 0;
for(int i = 0; i < n; i++) {
scanf("%d%d%d", &a, &b, &c);
b--, c--;
if(b < 0 || b >= m || c < 0 || c >= m) { //判斷b和c是否在範圍裏面
ans++;
continue;
}
if(a == 1) { //x和y屬於同一類的信息
if(same(b, c + m) || same(b, c + 2*m)) { //判斷b和c在之前的情況中,是否已構成捕食與被捕食的關係
ans++;
} else { //如果沒有捕食關係,則在3個區間中把b和c放在一起
unite(b, c);
unite(b + m, c + m);
unite(b + m*2, c + m*2);
}
} else { //x喫y的信息
if(same(b, c) || same(b, c + 2*m)) { //判斷b和c是否爲同一類或者已經構成c喫b的捕食關係,若是則爲錯ans+1
ans++;
} else {
unite(b, c + m);
unite(b + m, c + 2*m);
unite(b + 2*m, c);
}
}
}
printf("%d\n", ans);
return 0;
}
參考:挑戰程序設計競賽