poj-1182 食物鏈

食物鏈 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;
}

參考:挑戰程序設計競賽

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章