1. 程式人生 > >並查集(親戚)

並查集(親戚)

#include<iostream>
using namespace std;
int N ;
int parent[100];

void UFset()   //初始化
{
for(int i = 0;i < N;i++)
   parent[i]=-1;
}

int Find(int x)   //返回第x節點所屬集合的根結點
{
for(int i = x;parent[i] >= 0;i = parent[i]);//找到根i;
while(i != x )    //優化方案――壓縮路徑
{//其實這裡產生的效果是所有有親戚關係的人的parent都相同並且為根i;!!!
   int tmp=parent[x];
   parent[x] = i;
   x=tmp;
}
return i;
}

void Union(int R1,int R2)   //將兩個不同集合的元素進行合併,使兩個集合中任兩個元素都連通
{
int px = Find(R1);
int py = Find(R2);
if(px == py)//根相同不用處理!!!
   return;
int tmp = parent[R1]+parent[R2];//為什麼?????????????//tmp一定是負數!!!應為根都為負數!!R是否要改為P

if(parent[R1] > parent[R2]) //優化方案――加權法則
{
   parent[R1]=R2;//
   parent[R2]=tmp;
}
else
{
   parent[R2] = R1;
   parent[R1] = tmp;
}
}

int main()
{
int x , y;
int M ,P ;
cin >> N >> M ; //N個人M個親屬關係;
cin >> P;
UFset();//初始化

for(int i = 0 ;i < M ;i++ )
{
   cin >> x >> y ;
   Union(x , y) ; //關鍵!!!構造親戚關係!!!
}

    for( i = 0 ; i < P ;i++)
{
   cin >> x >> y ;
   if( Find(x) == Find(y) )
    cout<<"yes , relationt exist"<<endl;
   else
    cout<<"no , they are not relation"<<endl;
}
return 0 ;
}
/* 題目::
若某個家族人員過於龐大,要判斷兩個是否是親戚,確實還很不容易,現在給出某個親戚關係圖,求任意給出的兩個人是否具有親戚關係。
規定:x和y是親戚,y和z是親戚,那麼x和z也是親戚。如果x,y是親戚,那麼x的親戚都是y的親戚,y的親戚也都是x的親戚。
資料輸入:
第一行:三個整數n,m,p,(n<=5000,m<=5000,p<=5000),分別表示有n個人,m個親戚關係,詢問p對親戚關係。
以下m行:每行兩個數Mi,Mj,1<=Mi,Mj<=N,表示Ai和Bi具有親戚關係。
接下來p行:每行兩個數Pi,Pj,詢問Pi和Pj是否具有親戚關係。
資料輸出:
P行,每行一個’Yes’或’No’。表示第i個詢問的答案為“具有”或“不具有”親戚關係。
樣例:
input.txt
6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6
output.txt
Yes
Yes
No
 */

#include<iostream>
using namespace std;
int N,M,Q;
int pre[20000],rank[20000];
void makeset(int x)
 {
     pre[x]=-1;//開始都是根節點
     rank[x]=0;//深度都為0
 }
int find(int x)
 {
     int r=x;
     while(pre[r]!=-1) r=pre[r];//類似遞迴的加速查詢
     while(x!=r)//統一把根節點置為r(統一集合名稱)
      {
          int q=pre[x];
          pre[x]=r;
          x=q;
      }
    return r;      
 }    
void unionone(int a,int b)
 {
     int t1=find(a);
     int t2=find(b);
     if(rank[t1]>rank[t2])//?
       pre[t2]=t1;
    else
       pre[t1]=t2;
    if(rank[t1]==rank[t2])
      rank[t2]++;     
 }       
int main()
 
{
   int i,a,b,c,d;
    while(cin>>N>>M)
     {
         for(i=1;i<=N;i++)
          makeset(i);
        for(i=1;i<=M;i++)
          {
              cin>>a>>b;
              if(find(a)!=find(b))
               unionone(a,b);
          }
        cin>>Q; 
        for(i=1;i<=Q;i++)
         {
             cin>>c>>d;
             if(find(c)==find(d))
              cout<<"YES"<<endl;
             else
              cout<<"NO"<<endl; 
         }           
     }   
    return 0;
}


/* 題目: 親戚(Relations)
或許你並不知道,你的某個朋友是你的親戚。他可能是你的曾祖父的外公的女婿的外甥的表姐的孫子。如果能得到完整的家譜,判斷兩個人是否親戚應該是可行的,但如果兩個人的最近公共祖先與他們相隔好幾代,使得家譜十分龐大,那麼檢驗親戚關係實非人力所能及.在這種情況下,最好的幫手就是計算機。
為了將問題簡化,你將得到一些親戚關係的資訊,如同Marry和Tom是親戚,Tom和B en是親戚,等等。從這些資訊中,你可以推出Marry和Ben是親戚。請寫一個程式,對於我們的關心的親戚關係的提問,以最快的速度給出答案。
參考輸入輸出格式 輸入由兩部分組成。
第一部分以N,M開始。N為問題涉及的人的個數(1 ≤ N ≤ 20000)。這些人的編號為1,2,3,…,N。下面有M行(1 ≤ M ≤ 1000000),每行有兩個數ai, bi,表示已知ai和bi是親戚.
第二部分以Q開始。以下Q行有Q個詢問(1 ≤ Q ≤ 1 000 000),每行為ci, di,表示詢問ci和di是否為親戚。
對於每個詢問ci, di,若ci和di為親戚,則輸出Yes,否則輸出No。
樣例輸入與輸出
輸入relation.in
10 7
2 4
5 7
1 3
8 9
1 2
5 6
2 3
3
3 4
7 10
8 9
輸出relation.out
Yes
No
Yes
  */