1182 帶權並查集 (不一樣的做法)
阿新 • • 發佈:2018-12-14
今天在《挑戰程式設計競賽》中看到對於這題一種獨特的寫法,巧妙應用了並查集,於是理解後摘抄下來分享一下。
由於N和K很大,所以必須高效地維護動物之間的關係,並快速判斷是否產生了矛盾。並查集是維護 “屬於同一組” 的資料結構,但是在本題中,並不只有屬於同一類的資訊,還有捕食關係的存在。因此需要開動腦筋維護這些關係。
對於每隻動物i建立3個元素i-A, i-B, i-C, 並用這3*N個元素建立並查集。這個並查集維護如下資訊:
① i-x 表示 “i屬於種類x”。
②並查集裡的每一個組表示組內所有元素代表的情況都同時發生或不發生。
例如,如果i-A和j-B在同一個組裡,就表示如果i屬於種類A那麼j一定屬於種類B,如果j屬於種類B那麼i一定屬於種類A。因此,對於每一條資訊,只需要按照下面進行操作就可以了。
1)第一種,x和y屬於同一種類———合併x-A和y-A、x-B和y-B、x-C和y-C。
2)第二種,x吃y—————————合併x-A和y-B、x-B和y-C、x-C和y-A。
不過在合併之前需要先判斷合併是否會產生矛盾。例如在第一種資訊的情況下,需要檢查比如x-A和y-B或者y-C是否在同一組等資訊。(一開始我一直不明白,對於兩種資訊都是合併,那麼以後怎麼分清到底是同類還是捕食關係呢,或者說如何判斷是否會產生矛盾呢?後來發現,它利用3*N的陣列分3段1~N,N~2N,2N~3N分別當做是A、B、C三個種類的集合,把所有可能符合的情況都會匯入進去,雖然對於兩種資訊的操作都是合併,但合併的內容是不一樣的,這樣就可以在合併之前判斷其是否以另一種資訊合併過或者符合另一種資訊。可以自己舉例來理解一下)
#include<cstdio> #include<cstring> const int maxn=50005; int pre[3*maxn]; int find(int x) { return pre[x]==x?x:pre[x]=find(pre[x]); } int same(int x,int y) { if(find(x)==find(y)) return 1; return 0; } void merge(int x,int y) { x=find(x),y=find(y); if(x!=y) pre[x]=y; } int main() { int n,k,d,x,y; scanf("%d%d",&n,&k); for(int i=0;i<=3*n;i++) //元素x,x+N,x+2*N分別代表x-A,x-B,x-C pre[i]=i; int cnt=0; for(int i=0;i<k;i++) { scanf("%d%d%d",&d,&x,&y); if(x<0||x>n||y<0||y>n) { cnt++; continue; } if(d==1) //x和y為同類,不可能出現捕食或被捕食關係 { if(same(x,y+n)||same(x,y+2*n)) cnt++; else //因為不知道x,y為A,B,C中的哪一類 所以全部加入 { merge(x,y); merge(x+n,y+n); merge(x+2*n,y+2*n); } } else //當d==2時,x和y不可能是同類 或 被捕食關係 { if(same(x,y)||same(x,y+2*n)) cnt++; else //因為不知道x,y為A,B,C中的哪一類,所以存在捕食關係的全部加入 { merge(x,y+n); merge(x+n,y+2*n); merge(x+2*n,y); } } } printf("%d\n",cnt); return 0; }
這是我的一點見解,如果文中有誤或有更好的見解 請多多指教 q(≧▽≦q)
本文參考部落格