首先用floyd求出传递闭包,即用g[i][j]表示i向j打过电话,当且仅当个g[i][j] = g[j][i] = 1时,二者处于同一个电话圈。用并查集表示连通分量,然后输出各个连通分量。
#include<cstdio>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<iostream>
using namespace std;
vector<string> name;
vector<string> a[30]; //保存连通分量
int n, m;
int g[30][30];
int vis[30];
int f[30];
int find(int x){
return f[x] == x ? x : find(f[x]);
}
int id(string s){ //用id表示姓名
for(int i = 0; i < name.size(); ++i)
if(s == name[i]) return i + 1;
name.push_back(s);
return name.size();
}
int main(){
string s1, s2;
int kase = 0;
while(scanf("%d%d", &n, &m) == 2 && ( n || m)){
if(kase) cout << endl;
memset(g, 0, sizeof(g));
memset(vis, 0, sizeof(vis));
name.clear();
for(int i = 1; i <= n; ++i) f[i] = i;
for(int i = 0; i < m; ++i){
cin >> s1 >> s2;
g[id(s1)][id(s2)] = 1;
}
for(int k = 1; k <= n; ++k)
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
if(g[i][k] == 1 && g[k][j] == 1)
g[i][j] = 1;
printf("Calling circles for data set %d:\n", ++kase);
for(int i = 1; i <= n; ++i){
for(int j = i+1; j <= n; ++j)
if(g[i][j] == 1 && g[j][i] == 1){
int t1 = find(i);
int t2 = find(j);
if(t1 != t2) f[t2] = t1;
}
}
int t = 0;
for(int i = 1; i <= n; i++){
if(!vis[i]){
a[t].push_back(name[i-1]);
vis[i] = 1;
for(int j = 1; j <= n; j++)
if(find(i) == find(j) && !vis[j]){
a[t].push_back(name[j-1]);
vis[j] = 1;
}
t++;
}
}
for(int i = 0; i < t; ++i){
for(int j = 0; j < a[i].size(); ++j)
if(!j) cout << a[i][j];
else cout << ", " << a[i][j];
cout << endl;
}
for(int i = 0; i < t; i++)
a[i].clear();
}
return 0;
}