1. 程式人生 > >深入理解帶權並查集(例題+思考)

深入理解帶權並查集(例題+思考)

普通的並查集僅僅記錄的是集合的關係,這個關係無非是同屬一個集合或者是不在一個集合。而帶權並查集,不僅記錄集合的關係,還記錄著集合內元素的關係或者說是元素連線線的權值。這裡用三個例題講解一下吧。

How Many Answers Are Wrong (HDU - 3038)

題目大意:
給你一系列區間和,判斷給出的區間中有幾個是不合法的。
思考:
1.如何建立區間之間的聯絡
2.如何發現悖論
首先是如何建立聯絡,我們可以用一張圖表示
這裡寫圖片描述
假如說區間【fx,x】是之前建立的區間,他們之間和為sum[x],fx和x的聯絡可以用集合來儲存,同理【fy,y】也是如此。當給出了一個新的區間【x,y】時,且區間和為s。就產生了兩種情況了,如果fx == fy 那麼這兩個區間是有關聯的區間,也就是【x,y】之間的和是可以求出的。可以把這個圖看成一個向量。 區間【x,y】的和就是可以寫成sum[x] - sum[y]。判斷給出的s與向量法計算的區間和是否相等就可以判斷是否是悖論。
當然如果fx != fy就需要建議新的區間關係。首先將fy指向fx,這代表fx是區間的左端點,計算sum【fy】= sum【x】- sum【y】+ s;這裡同樣用的是向量法。
這樣建立聯絡與判斷悖論都可以表達了,接下來就是一些細節了,比如在更新區間的時候要進行路徑的壓縮,壓縮的過程中需要對權值進行更新,目的是使每個已知區間最大化。
程式碼

#include <cstdio>

const int maxn = 200000 + 10;

int pre[maxn];
int sum[maxn];    // sum是該節點到其根的和,比如說sum[3],3的根是1,那麼sum[3]表示的就是1到3的和……

int find(int x) {
    if (x == pre[x])  return x;
    else {
        int root = find(pre[x]);     // 找到根節點
        sum[x] += sum[pre[x]];       // 權值合併,更新
        return
pre[x] = root; // 壓縮路徑 } } int main() { int n, m; while(~scanf("%d%d", &n, &m)) { for(int i = 0; i <= n; i++) { pre[i] = i; sum[i] = 0; } int x, y; int s; int ans = 0; while(m--) { scanf("%d%d%d"
, &x, &y, &s); x--; // 想一下為什麼要減一,可以讓類似【1,5】【6,10】這樣的區間可以合併…… int fx = find(x); int fy = find(y); if (fx != fy) { pre[fy] = fx; sum[fy] = sum[x] - sum[y] + s; } else if (sum[y] - sum[x] != s) ans++; } printf("%d\n", ans); } return 0; }

有了這道題的基礎我們可以將帶權並查集升級成種類並查集

比如所這道題

A Bug’s Life POJ - 2492

每次給出兩個昆蟲的關係(異性關係),然後發現這些條件中是否有悖論
就比如說第一組資料

1 2
2 3
1 3
1和2是異性,2和3是異性,然後說1和3是異性就顯然不對了。

我們同樣可以思考一下這道題如何用帶權並查集去做。
這裡寫圖片描述
首先用r[x]儲存的是x與其根節點rx的關係,0代表同性1代表異性(其實反著也一樣因為這個關係是一個環狀的)
這道題與上一道題唯一的不同是權值不是累加的關係而是相當於二進位制的個位,也就是累加結果取%2。
這樣就很容易仿照上一道題寫出一下程式碼

#include <cstdio>

const int maxn = 2000 + 10;

int pre[maxn];
int r[maxn];  // 與根節點的關係,如果值為1則為異性如果為0則為同性

int find(int x) {
    int t = pre[x];
    if (pre[x] != x) {
        pre[x] = find(pre[x]);
        r[x] = (r[x] + r[t]) % 2;  // 更新關係
    }
    return pre[x];
}

int main() {
   // freopen("input.txt", "r", stdin);
    int flag;
    int kase = 0;
    int T;
    int x, y;
    int n, m;
    scanf("%d", &T);
    while(T--) {
         flag = 1;
         scanf("%d%d", &n, &m);
         for(int i = 1; i <= n; i++) {
             pre[i] = i;
             r[i] = 0;
         }
         while(m--) {
             scanf("%d%d", &x, &y);
             if (!flag) continue;
             int rx = find(x);
             int ry = find(y);
             if (rx == ry) {
                if ((r[x] - r[y]) % 2 == 0) {
                    flag = 0;
                }
             }
             else {
                 pre[rx] = ry;
                 r[rx] = (r[x] - r[y] + 1) % 2;
             }
         }
         printf("Scenario #%d:\n",++kase);
         if(flag)
                printf("No suspicious bugs found!\n\n");
         else
                printf("Suspicious bugs found!\n\n");
    }

    return 0;
}

我們再把上一道題升級一下,從兩個種類拓展為三個種類,由於三個種類的關係依舊是一個所以依然可以套帶權並查集模版。有幾個種類就取幾模,這裡是%3

食物鏈 POJ - 1182

這裡給出三種生物的關係,吃與同類的關係。由於這三種生物的關係依舊可以形成一個環,A吃B,B吃C,C又吃A。所以可以套種類並查集模版。
程式碼
這裡寫圖片描述

#include<stdio.h>

const int maxn = 100000 + 10;

int pre[maxn], r[maxn];    // 父節點,與父節點的關係。0代表同類,1代表吃父節點,2代表被父節點吃。

int Find(int x)
{
    int t = pre[x];
    if(pre[x] != x)
    {
        pre[x] = Find(pre[x]);          // 壓縮路徑
        r[x] = (r[t] + r[x]) % 3;       // 更新關係
    }
    return pre[x];
}
int main()
{
    freopen("input.txt", "r", stdin);
    int i, N, T, ans;
    scanf("%d%d", &N, &T);
    for(i=0; i<=N; i++) {
        pre[i] = i;
        r[i] = 0;
    }
    ans = 0;
    while(T--)
    {
        int x, y, d;
        scanf("%d%d%d", &d, &x, &y);

        int rx = Find(x);
        int ry = Find(y);
        if(x>N || y>N || (d==2 && x==y) )
            ans++;
        else if(rx == ry && (r[x] - r[y] + 3)%3 != d - 1)
            ans++;
        else if(rx != ry) {
            pre[rx] = ry;
            r[rx] = ((d-1) + r[y] - r[x] + 3) % 3;
        }
    }
    printf("%d\n", ans);
    return 0;
}

ps

這篇文章有什麼錯誤的地方歡迎指正……

相關推薦

深入理解例題+思考

普通的並查集僅僅記錄的是集合的關係,這個關係無非是同屬一個集合或者是不在一個集合。而帶權並查集,不僅記錄集合的關係,還記錄著集合內元素的關係或者說是元素連線線的權值。這裡用三個例題講解一下吧。 How Many Answers Are Wrong (HDU

POJ-1984-Navigation Nightmare+中級

sof 走了 Go problem name 更新 討論 nio scan 傳送門:Navigation Nightmare 參考:1:https://www.cnblogs.com/huangfeihome/archive/2012/09/07/2675123.html

HDU 3047 Zjnu Stadium,難想到

union blank then submit urn lin lock sts count M - Zjnu Stadium Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d

1182 不一樣的做法

食物鏈 今天在《挑戰程式設計競賽》中看到對於這題一種獨特的寫法,巧妙應用了並查集,於是理解後摘抄下來分享一下。 由於N和K很大,所以必須高效地維護動物之間的關係,並快速判斷是否產生了矛盾。並查集是維護 “屬於同一組” 的資料結構,但是在本題中,並不只有屬於同一類的資訊,還

HDU 3635 Dragon Balls!!!

Dragon Balls Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 7926    Accepted

並查集 並查集是一個很高效演算法,理解起來也很簡單,寫起來更簡單。 ①fat[i] = i; ②找到一個點的祖先 int findfat(int x)   {       if(fat[x] == x) return x;       return fi

POJ2492_註釋很詳細

/* 題目大意:有一個教授正在研究一種昆蟲,他認為這種昆蟲之中沒有同性戀。 有n只這種昆蟲,他們之間有k個關係(不知道他們是什麼性別) 這k個關係大概是a與b交往(教授認為只有異性可以交往) 問給的這組資料能否支援教授的觀點 解題思路:可以用帶權並查集來求解

種類的簡單總結順帶總結

並查集是一種樹型的資料結構,一般用於處理一些不相交集合(Disjoint Sets)的合併及查詢問題,對於普通的並查集我們一般分為三個部分——初始化,查詢,合併。 初始化:把每個點所在集合初始化為其自身(即每個元素單獨構成一個集合,其父結點是其本身)。 查詢:查詢元素所在

HDU3635 Dragon Balls

align size any cat webkit city ret follow num 題目鏈接:   http://acm.hdu.edu.cn/showproblem.php?pid=3635 題目描述: Dragon Balls Problem Descript

——poj2236

algorithm blog oid poj2236 stream 命令 net 最大 spa 題目:Wireless Network 題意:給定n臺已損壞計算機的位置和計算機最遠通信距離d,然後分別根據命令執行以下兩種操作: "O p" (1 <= p <=

HDU - 3038 / 3048 待補

hdu return bsp value 題目 ios i++ mes sca 題目鏈接:點我點我 題意: 題解: 兩題代碼差不多,放個3047的。 1 #include <cstdio> 2 #include <iostream>

HDU 3038 How Many Answers Are Wrong

define pro tor memset set sizeof fin printf class 題目鏈接:http://acm.split.hdu.edu.cn/showproblem.php?pid=1272 題目大意:有n條信息,每條信息都給出區間l到r的值,如果

POJ 1182 食物鏈

動物 return i++ rip width d+ oid body 思維 食物鏈 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 78551 Accepte

poj1703 Find them, Catch them

har find 輸出 scanf -- oot n) puts stdin 題目鏈接 http://poj.org/problem?id=1703 題意 有兩個幫派:龍幫和蛇幫,兩個幫派共有n個人(編號1~n),輸入m組數據,每組數據為D [a][b]或A [a][

poj2492 A Bug's Life

tab set 復雜 鏈接 image 之間 判斷 clu 成了 題目鏈接 http://poj.org/problem?id=2492 題意 蟲子有兩種性別,有n只蟲子,編號1~n,輸入m組數據,每組數據包含a、b兩只蟲子,表示a、b為不同性別的蟲子,根據輸入的m組

poj1182 食物鏈

txt amp 帶權並查集 htm 代碼 || blog 參考 oid 題目鏈接 http://poj.org/problem?id=1182 思路 前面做的帶權並查集的權值記錄該結點與其父結點是否是同一類,只有兩種取值情況(0,1),在這題中某結點a和其父結點b的取

POJ 1988 Cube Stacking

ons from += same file rep stream scribe 並查集 Farmer John and Betsy are playing a game with N (1 <= N <= 30,000)identical cubes label

HihoCoder - 1515 分數調查

amp code space blog contain script += esp 判斷 小Hi的學校總共有N名學生,編號1-N。學校剛剛進行了一場全校的古詩文水平測驗。 學校沒有公布測驗的成績,所以小Hi只能得到一些小道消息,例如X號同學的分數比Y號同學的分數高S分

食物鏈

clu style org 之間 i++ init esc rip 但是 題目鏈接:http://poj.org/problem?id=1182 題目: Description 動物王國中有三類動物A,B,C,這三類動物的食物鏈構成了有趣的環形。A吃B, B吃C,C吃

HDU1289 A Bug's Life

clu some assume ever different 合並操作 ports main separate HDU1289 帶權並查集 Problem Description Background Professor Hopper is researching the