此題按照《挑戰程式設計競賽(第2版)》P89的解法,不容易想到,但想清楚了代碼還是比較直覺的。
并查集模闆(包含了記錄高度的rank數組和查詢時狀态壓縮)
并查集實作
并查集是用于維護“屬于同一集合”的資料結構,然而這道題的“屬于同一集合”并不指“是同類”,而是指“這幾個情況若發生必然同時發生”。
我們從了解題意開始:
有N隻動物,分别編号為1,2,...,N。所有動物屬于A,B,C類中的一種,類之間有天然的A吃B,B吃C,C吃A的關系。
按如下兩種格式順序給出共K條資訊(隻表達了相對關系):
1 x y: x,y是同類
2 x y: x吃y
每條資訊若不符合常理(編号大于N,或自己吃自己)或與已有資訊沖突,則為錯誤。
問這K條資訊有幾條錯誤?
由于A,B,C之間的吃與被吃關系構成一個循環,是以三類的等級關系根本上也是相對的,那麼每條資訊都可以翻譯成三種可能的實際情況。同時維護這三種可能
,就需要為每個動物x配置設定三個數組元素,分别表示x是A,x是B,x是C這三個事件。
同屬一個集合的事件意味着“若發生必然同時發生”。
并查集需要用一個數組存儲每個元素“屬于哪一集合”,那麼可以開一個長度為N*3的數組,用x,x+N,x+N*2分别表示x是A,x是B,x是C。
表示x與y是同類,需要維護這三種可能
unite(x,y); //x,y都是A
unite(x+n,y+n); //x,y都是B
unite(x+2*n,y+2*n); //x,y都是C
表示x吃y,需要維護這三種可能
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
想清楚了道理,代碼就比較好了解了。注意由于從始至終隻知道相對關系,同時維護了三種可能,是以判斷沖突的時候任選一種判斷就可以了。
OJ運作結果如下:
這道題使我對并查集有了新的認識,想清楚“同屬一個集合”代表什麼很重要,不一定就是題目限定的“屬于同一種”。
分析問題的難度有時會大于程式設計的難度,如果說代碼能力可以通過刷題習得,那麼分析問題的能力真的需要足夠的知識儲備和一些“創造性思維”了。