POJ -- 1182 食物鏈

題目網址 :POJ -- 1182ios

這是一道利用並查集模擬的題目,也是並查集的裸題。spa

乍一看好像有點麻煩,由於會以爲動物們來回吃來吃去狀況就會變得愈來愈複雜。code

其實不管X和Y是同類仍是捕食關係,歸根結底都是集合合併的問題(捕食關係「錯位」一下也是對等合併)。ci

考慮何時會發生衝突?衝突的狀況只有一個,那就是兩個有交集的食物鏈合併,而且合併的時候發生錯位,也就是ABC不對等合併。另外的狀況都是元素X所在的食物鏈和元素Y所在的食物鏈沒有交集,這樣合併後更新就好。get

舉個例子,就是你能夠ABC和ABC合併, 也能夠ABC和DEF合併,可是絕對不能ABC和BCA或者ACB合併。 緣由是你如今獲取了兩個元素X和Y,他們倆要麼處於同一個食物鏈中,要麼處於不一樣食物鏈中,不一樣食物鏈能夠直接合並,相同食物鏈只能對等合併(其實也就是不用合併),不能錯位合併。io

並查集是一種樹結構,有一個特別典型的特性:只要不是根節點,就先獲取根節點再作處理。stream

其實看過並查集的實現就會發現,若是一個節點不是根節點,那麼跟這個節點有關的信息都是沒意義的。由於並查集只保證根節點的信息具備時效性,其餘非根節點的節點都只有一個做用,那就是找到根節點。nio

貼下代碼,這個模擬的過程仍是比較噁心人的。next

#include <cstdio>
#include <iostream>
using namespace std;
const int maxn = 50050;
int p[maxn], rank[maxn], prev[maxn], next[maxn];
void makeset(int x)
{
    p[x] = x;
    rank[x] = 1;
}
int findset(int x)
{
    int px = x, i;
    while(p[px] != px) px = p[px];
    while(x != px)
    {
        i = p[x];
        p[x] = px;
        x = i;
    }
    return px;
}
int unionset(int x, int y)
{
    x = findset(x);
    y = findset(y);
    if(rank[x] > rank[y])
    {
        p[y] = x;
        return x;
    }
    else
    {
        p[x] = y;
        if(rank[x] == rank[y]) rank[y]++;
        return y;
    }
}

int main()
{
    // freopen("in.txt", "r", stdin);
    int N, K;
    cin >> N >> K;
    for(int i = 1; i <= N; i++) makeset(i);
    int ans = 0;
    for(int i = 0; i < K; i++)
    {
        int D, X, Y;
        scanf("%d%d%d", &D, &X, &Y);
        if(X > N || Y > N) { ans++; continue; }
        if(D == 1)
        {
            int x = findset(X), y = findset(Y);
            if(x == y) continue;
            if(y == prev[x] || y == next[x]) ans++;
            else
            {
                int a = prev[x], b = x, c = next[x];
                int d = prev[y], e = y, f = next[y];
                int o, p, q;

                if(a == 0) o = d;
                else if(d == 0) o = a;
                else o = unionset(a, d);

                p = unionset(b, e);

                if(c == 0) q = f;
                else if(f == 0) q = c;
                else q = unionset(c, f);

                prev[p] = o; next[p] = q;
                prev[o] = q; next[o] = p;
                prev[q] = p; next[q] = o;
            }
        }
        else
        {
            int y = findset(Y), x = findset(X);
            if(x == y) ans++;
            else
            {
                int a = x, b = next[x], c = prev[x];
                int d = prev[y], e = y, f = next[y];

                if(e == c || e == a) ans++;
                else
                {
                    int o, p, q;
                    if(b == 0) p = e;
                    else p = unionset(b, e);

                    if(d == 0) o = a;
                    else o = unionset(a, d);

                    if(c == 0) q = f;
                    else if(f == 0) q = c;
                    else q = unionset(c, f);

                    prev[p] = o; next[p] = q;
                    prev[o] = q; next[o] = p;
                    prev[q] = p; next[q] = o;
                }
            }
        }
    }
    cout << ans << endl;
    return 0;
}

記得對每一個元素保存捕食它的集合和它捕食的集合,集合固然都是根節點,並查集保證這一點。集合

相關文章
相關標籤/搜索