poj1703(各種姿勢)


題目鏈接:http://poj.org/problem?id=1703

 

題意:有n個人分別屬於兩個團伙,接下來m組形如 ch, x, y的數據,ch為“D"表示 x, y屬於不同的團伙,ch為"A"表示詢問x,y書否屬於同一個團伙;

 

解法1:我們可以用jion(x, y)屬於同一個團伙,jion(x+n, y)表示x屬於第二個團伙,y屬於第一個團伙,jion(x, y+n)表示x屬於第一個團伙,y屬於第二個團伙;

那么對於每組不同團伙的x, y我們只需要jion(x+n, y) ,jion(x, y+n)即可;查詢時判斷x,y或者x+n, y+n根節點是否相同即可,因為集合關系jion表示屬於同一團伙,根節點相同則屬於相同團伙,若x, y+n,或者x+n, y根節點相同則屬於不同團伙,其余情況即為不能確定;

 

代碼:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #define MAXN (100000+10)  //***MAXN后面做下標時MAXN*2,要加括號,不然會越界!!run time error!!!
 5 using namespace std;  6 
 7 int pre[MAXN*2], rank[MAXN*2]; //***rank用來區分樹的高度,但其不存儲樹的具體高度
 8 
 9 int find(int x){ 10     int r = x; 11     while(pre[r]!=r){ 12         r = pre[r]; 13  } 14     int i = x;      //****路徑壓縮
15     while(pre[i]!=r){ 16         int gg = pre[i]; 17         pre[i] = r; 18         i = gg; 19  } 20     return r; 21 } 22 
23 void jion(int x, int y){ 24     int xx = find(x); 25     int yy = find(y); 26     if(rank[xx]>rank[yy]){   //***啟發式合並,就是把矮的樹合並到高的樹地下,把合並時間從0(n)降到o(logn)
27         pre[yy] = xx; 28     }else{ 29         pre[xx] = yy; 30         if(rank[xx] == rank[yy]){ //**若樹的標記高度一樣,那么給合並后作為父親的樹rank+1,以區分樹的高度 31             rank[xx]++; 32  } 33  } 34 } 35 
36 int main(void){ 37     int t; 38     scanf("%d", &t); 39     while(t--){ 40         int n, m; 41         scanf("%d%d", &n, &m); 42         for(int i=1; i<=2*n; i++){ 43             pre[i] = i; 44             rank[i] = 0; 45  } 46         while(m--){ 47             char ch[2]; 48             int x, y; 49             scanf("%s%d%d", ch, &x, &y); 50             if(ch[0]=='D'){ 51                 jion(x, y+n); 52                 jion(x+n, y); 53             }else{ 54                 if(find(y+n)==find(x)||find(x+n)==find(y)){ 55                     printf("In different gangs.\n"); 56                 }else if(find(x)==find(y)||find(x+n)==find(y+n)){ 57                     printf("In the same gang.\n"); 58                 }else{ 59                     printf("Not sure yet.\n"); 60  } 61  } 62  } 63  } 64     return 0; 65 }

 

方法2:用vis數組標記不同的集合,如:vis[x]=y,表示與x不同集合的點y;

用並查集合並屬於同一類的點集;

代碼:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #define MAXN 100010
 4 using namespace std;  5 
 6 int pre[MAXN], vis[MAXN], rank[MAXN]; //***vis標記不同集合的編號,rank區分樹高  7 //***vis[x]=y,表示記錄x與y不同集合,相當於無向圖,所以需雙向標記
 8 
 9 /*int find(int x){ //**400+ms 10  int r = x; 11  while(pre[r]!=r){ 12  r = pre[r]; 13  } 14  int i = x; 15  while(i!=r){ 16  int gg = pre[i]; 17  pre[i] = r; 18  i = gg; 19  } 20  return r; 21 }*/
22 
23 int find(int x){ //**306ms (再加啟發式合並=282ms)
24     return pre[x]==x ? x : pre[x] = find(pre[x]); 25 } 26 
27 void jion(int x, int y){ 28     int xx = find(x); 29     int yy = find(y); 30     if(rank[xx] > rank[yy]){ 31         pre[yy] = xx; 32     }else{ 33         pre[xx] = yy; 34         if(rank[xx] == rank[yy]){ 35             rank[yy]++; 36  } 37  } 38 } 39 
40 int main(void){ 41     int t; 42     scanf("%d", &t); 43     while(t--){ 44         int n, m; 45         scanf("%d%d", &n, &m); 46         for(int i=1; i<=n; i++){ 47             pre[i] = i; 48             vis[i] = 0; 49             rank[i] = 0; 50  } 51         while(m--){ 52             char ch[2]; 53             int x, y; 54             scanf("%s%d%d", ch, &x, &y); 55             if(ch[0]=='D'){ 56                 if(vis[x]==0 && vis[y]==0){  //**x, y都沒出現過
57                     vis[x] = y; 58                     vis[y] = x; 59                 }else if(vis[x]==0){ //**x沒出現過
60                     vis[x] = y; 61  jion(x, vis[y]); 62                 }else if(vis[y]==0){ //**y沒出現過
63                     vis[y] = x; 64  jion(y, vis[x]); 65                 }else{               //**都出現過
66  jion(x, vis[y]); 67  jion(y, vis[x]); 68  } 69             }else{ 70                 if(find(x)==find(y)){ 71                     printf("In the same gang.\n"); 72                 }else if(find(x)==find(vis[y])){ 73                     printf("In different gangs.\n"); 74                 }else{ 75                     printf("Not sure yet.\n"); 76  } 77  } 78  } 79  } 80     return 0; 81 }

 

方法3:

種類並查集,先區分能不能辨別的情況,然后只要考慮同和不同兩種情況,可以用rank數組記錄當前節點x與其根節點是否相同的信息,1表相同,0表不同;

 

代碼:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #define MAXN 100010
 4 using namespace std;  5 
 6 int pre[MAXN], rank[MAXN]; //**rank儲存x與x的根節點是否相同的信息,1表相同,0表不同
 7 
 8 int find(int x){  9     if(x==pre[x]){ 10         return pre[x]; 11  } 12     int xx = pre[x]; 13     pre[x] = find(pre[x]); 14     rank[x] = (rank[x] + rank[xx])&1; //**壓縮路徑,x的根節點改變了,rank[x]也要改變
15     return pre[x]; 16 } 17 
18 void jion(int x, int y){ 19     int xx = find(x); 20     int yy = find(y); 21     if(xx!=yy){ 22         pre[yy] = xx; 23         rank[yy] = (rank[x] + rank[y] + 1)&1; //**合並只需改變yy之前的rank值
24  } 25 } 26 
27 int main(void){ 28     int t; 29     scanf("%d", &t); 30     while(t--){ 31         int n, m; 32         scanf("%d%d", &n, &m); 33         for(int i=1; i<=n; i++){ 34             pre[i] = i; 35             rank[i] = 0; 36  } 37         while(m--){ 38             char ch[2]; 39             int x, y; 40             scanf("%s%d%d", ch, &x, &y); 41             if(ch[0]=='D'){ 42  jion(x, y); 43             }else{ 44                 if(find(x)==find(y)){ 45                     if(rank[x]==rank[y]){ 46                         printf("In the same gang.\n"); 47                     }else{ 48                         printf("In different gangs.\n"); 49  } 50                 }else{ 51                     printf("Not sure yet.\n"); 52  } 53  } 54  } 55  } 56     return 0; 57 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM