题意:
有两黑帮,N个人,现在又一些信息,你需要根据这些信息确定这两个人之间是否属于同一个黑帮。有两种输入: D a b 表示a,b不属于同一个黑帮;A a b 询问a,b是否属于同一个黑帮,可以回答属于,不属于,不确定三种。
题解:
可以用带权的并查集解决。若a,b同属于一个集合说明二者的关系已经确定,输出属于或者不属于;否则输出不确定。当a,b同属于一个集合时,可以通过权值来判断它们的具体关系。注意:这里的权值仅仅代表某个节点在压缩森林中与其根节点的距离,并不代表它在原森林中的深度。
#include<cstdio> using namespace std; #define MAX 200000 int father[MAX], weight[MAX], rank[MAX]; bool check[MAX]; void init ( int n ) { for ( int i = 1; i <= n; i++ ) { father[i] = i; weight[i] = 0; rank[i] = 1; check[i] = false; } } int Find ( int x, int& w ) //节点到压缩森林中根节点的距离 = 父节点到根的距离 + 本身到父节点的距离 { if ( father[x] != x ) { father[x] = Find ( father[x], w ); weight[x] = (weight[x] + w) % 2; w = weight[x]; } else w = 0; return father[x]; } int Find_Depth ( int x ) { int a, w; if ( x == father[x] ) //根节点到自己的距离为0,直接返回 return weight[x]; a = Find ( x, w ); return weight[a] + weight[x]; //节点到压缩森林中根节点的距离 = 父节点的距离 + 本身到父节点的距离 } void Link ( int x, int y ) //按秩结合 { int a, b, w; a = Find ( x, w ); b = Find ( y, w ); if ( a == b ) return; if ( rank[a] >= rank[b] ) { weight[b] = (weight[x] + 1 + weight[y]) % 2; rank[a] += rank[b]; father[b] = a; } else { weight[a] = (weight[x] + 1 + weight[y]) % 2; rank[b] += rank[a]; father[a] = b; } } int main() { char ch[4]; int n, m, a, b, w, cs; scanf("%d",&cs); while ( cs-- ) { scanf("%d%d",&n,&m); init ( n ); while ( m-- ) { scanf("%s %d %d",ch, &a, &b); if ( ch[0] == 'D' ) Link ( a, b ); else { if ( Find(a,w) != Find(b,w) ) printf("Not sure yet.\n"); else if ( Find_Depth(a) % 2 == Find_Depth(b) % 2) printf("In the same gang.\n"); else printf("In different gangs.\n"); } } } return 0; }
在网上还看到另一种解法:
#include <iostream> using namespace std; #define N 100005 int opp[N], father[N], rank[N]; int n, m; void make_set ( int x ) { for ( int i = 0; i <= x; ++i ) { father[i] = i; opp[i] = rank[i] = 0; } } int find_set ( int x ) { if ( father[x] != x ) father[x] = find_set(father[x]); return father[x]; } void Union ( int x, int y ) { int fx = find_set(x); int fy = find_set(y); if ( fx == fy ) return; if ( rank[fx] > rank[fy] ) father[fy] = fx; else father[fx] = fy; if ( rank[fx] == rank[fy] ) rank[fy]++; } int main() { int t, n, m, x, y; char ask; scanf("%d",&t); while ( t-- ) { scanf("%d%d",&n,&m); make_set(n); while ( m-- ) { getchar(); scanf("%c",&ask); scanf("%d%d",&x,&y); if ( ask == 'A' ) { if ( find_set(x) == find_set(y) ) printf("In the same gang.\n"); else if ( find_set(x) == find_set(opp[y]) ) printf("In different gangs.\n"); else printf("Not sure yet.\n"); } else { if ( opp[x] == 0 && opp[y] == 0 ) { opp[x] = y; opp[y] = x; } else if ( opp[x] != 0 && opp[y] == 0 ) { opp[y] = x; Union(y,opp[x]); } else if ( opp[x] == 0 && opp[y] != 0 ) { opp[x] = y; Union(x,opp[y]); } else if ( opp[x] != 0 && opp[y] != 0 ) { Union(x,opp[y]); Union(y,opp[x]); } } } } return 0; }