1. 程式人生 > >POJ 1182 食物鏈 [資料結構-並查集 union-find sets]

POJ 1182 食物鏈 [資料結構-並查集 union-find sets]

   在輸入時可以先判斷題目所說的條件2和3,即:
       1>若(x>n||y>n):即當前的話中x或y比n大,則假話數目num加1.
       2>若(x==2&&x==y):即當前的話表示x吃x,則假話數目num加1.
   而不屬於這兩種情況外的話語要利用並查集進行判斷當前的話是否與此前已經說過的話相沖突.
   struct node
   {
       int parent;                     //p[i].parent表示節點i的父節點
       int relation;                   //p[i].relation表示節點i與其父節點(即p[i].parent)的關係
   }p[50005];
   此處relation有三種取值(假設節點x的父節點為rootx,即p[x].parent=rootx):
   p[x].relation=0   ……》表示節點x與其父節點rootx的關係為同類
   p[x].relation=1   ……》表示節點x的父親節點rootx吃節點x,即節點x被吃
   p[x].relation=2   ……》表示節點x吃它的父親節點rootx,即節點x吃
   
   初始化函式為:void init_set(int n)
               {
                   int i;
                   for(i=1;i<=n;i++)
                   {
                       p[i].parent=i;            //初始時集合編號就設定為自身
                       p[i].relation=0;        //因為p[i].parent=i,即節點i的父親節點就是自身,所以此時節點i與其父親節點的關係為同類(即p[i].relation=0)
                   }
               }

   下面為並查集的兩個重要操作:查詢和合並.
   在查詢時因為節點不僅有父親節點域,而且還有表示節點與其父親節點關係的域,查詢過程中對父親節點域的處理和平常的處理一樣,即在查詢過程中同時實現路徑壓縮,但正是由於路徑壓縮,使得表示節點與其父親節點的關係的域發生變化,所以相應查詢節點表示其與父親節點的關係的域也要發生變化(因為路徑壓縮之前節點x的父親節點為rootx的話,那麼在路徑壓縮之後節點x的父親節點就變為了節點rootx的父親節點rootxx,所以此時p[x].relation儲存的是節點x與現在父親節點rootxx的關係),此處可以畫圖理解一下:


    很明顯查詢之前節點x的父親節點為rootx,假設此時p[x].relation=1(即表示x的父親節點rootx吃x)且p[rootx].relation=0(即表示rootx和其父親節點rootxx是同類),由這兩個關係可以推出rootxx吃x,而合併以後節點x的父親節點為rootxx(實現了路徑壓縮),且節點x的父親節點rootxx吃x,即查詢之後p[x].relation=1,很容易的推出合併之後的p[x].relation=(p[rootx].relation+p[x].relation)%3.

     在將元素x與y所在的集合合併時,假設元素x所在的集合編號為rootx,元素y所在的集合編號為rooty,合併時直接將集合rooty掛到集合rootx上,即p[rooty].parent=rootx,此時原來集合rooty中的根節點rooty的relation域也應隨之發生變化,因為合併之前rooty的父親節點就是其自身,故此時p[rooty].relation=0,而合併之後rooty的父親節點為rootx,所以此時需判斷rootx與rooty的關係,即更新p[rooty]的值,同理畫圖理解:
     
   此時假設假設p[x].relation=0(即x與rootx的關係是同類),p[y].relation=1(即rooty吃y),則有:
        1>輸入d=1時,即輸入的x和y是同類,則有上述關係可以推出rooty吃rootx,即p[rooty].relation=2;
        2>輸入d=2時,即輸入的x吃y,則有上述關係可以推出rooty與rootx是同類(因為rooty吃y,x吃y,則rooty與x是同類,又rootx與x是同類),即p[rooty].relation=0;
   當然,這只是一種可能,其它的可能情況和上面一樣分析,則可以得知:p[rooty].relation=(3+(d-1)+p[x].relation-p[y].relation)%3.

   當元素x與元素y在同一集合時,則不需要合併,因為此時x與y的父親節點相同,可以分情況討論:
        1>d=1時,即x與y是同類時,此時要滿足這要求,則必須滿足p[x].relation=p[y].relation,這很容易推出來.
        2>d=2時,即表示x吃y,此時要滿足這要求,則必須滿足(p[y].relation-p[x].relation+3)%3=1,如x和root是同類(即p[x].relation=0),此時要滿足x吃y,則必須滿足root吃y,即p[y].relation=1,可以像上面一樣畫圖來幫助理解.

警告:一下程式碼如果你拿去poj提交結果就是WA,必需修改成單組資料輸入。最後換行去掉才能AC。