本题中两点A,B在一个一个calling circle中的条件是存在一条A->B的路径,也存在一条B->A的路径。可以用Floyd算法传递闭包,
g[i][j] 为1表示i->j存在路径,反之表示不存在,那么AB在一个circle中等价于 g[A][B]&&g[B][A]。
可以用DFS或者并查集来输出连通分量。
本题的顶点以string表示,可以用STL map建立一个姓名到数字的映射,再建立一个数字到名字的映射,便于建图和输出。
DFS版本(简单,推荐)(我也不知道我开始的时候怎么想的非要用并查集==,超麻烦的)。
#include<iostream>
#include<cstring>
#include<map>
#include<string>
#include<vector>
#include<algorithm>
#define maxn 30
using namespace std;
int n, m,g[maxn][maxn],vis[maxn];
map<string, int> nton;
map<int, string> rev;
vector<vector<int> > ans;
void dfs(int cur, int flag)
{
if (flag) cout << ", ";
cout << rev[cur];
vis[cur] = 1;
for (int j = 0; j < n; j++)
if (!vis[j] && g[cur][j] && g[j][cur])
dfs(j, 1);
}
int main()
{
int kase = 0;
while (cin>>n>>m && n)
{
memset(g, 0, sizeof(g));
nton.clear();
rev.clear();
int ncnt = 0,t1,t2;
string n1, n2;
for (int i = 0; i < m; i++)
{
cin >> n1 >> n2;
if (nton.count(n1)) t1 = nton[n1];
else t1 = nton[n1] = ncnt++;
if (nton.count(n2)) t2 = nton[n2];
else t2 = nton[n2] = ncnt++;
g[t1][t2] = 1;
}
for (int k = 0; k < n; k++)
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
g[i][j] = g[i][j] || (g[i][k] && g[k][j]);
for (auto it = nton.begin(); it != nton.end(); it++)
rev[it->second] = it->first;
memset(vis, 0, sizeof(vis));
if (kase) cout << endl;
cout << "Calling circles for data set " << ++kase << ':' << endl;
for(int i=0;i<n;i++)
if (!vis[i])
{
dfs(i, 0);
cout << endl;
}
}
return 0;
}
并查集版本
#include<iostream>
#include<cstring>
#include<map>
#include<string>
#include<vector>
#include<algorithm>
#define maxn 30
using namespace std;
int n, m,g[maxn][maxn],ufs[maxn];
int find(int y)
{
return ufs[y] < 0 ? y : (ufs[y] = find(ufs[y]));
}
void setUnion(int r1, int r2)
{
if (r1 == r2) return;
if (ufs[r1] < ufs[r2])
{
ufs[r1] += ufs[r2];
ufs[r2] = r1;
}
else
{
ufs[r2] += ufs[r1];
ufs[r1] = r2;
}
}
int main()
{
int kase = 0;
while (cin>>n>>m && n)
{
memset(g, 0, sizeof(g));
map<string, int> nton;
map<int,string> rev;
int ncnt = 0,t1,t2;
string n1, n2;
vector<vector<int> > ans;
for (int i = 0; i < m; i++)
{
cin >> n1 >> n2;
if (nton.count(n1)) t1 = nton[n1];
else t1 = nton[n1] = ncnt++;
if (nton.count(n2)) t2 = nton[n2];
else t2 = nton[n2] = ncnt++;
g[t1][t2] = 1;
}
for (int k = 0; k < n; k++)
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
g[i][j] = g[i][j] || (g[i][k] && g[k][j]);
for (auto it = nton.begin(); it != nton.end(); it++)
rev[it->second] = it->first;
memset(ufs, -1, sizeof(ufs));
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++)
if (g[i][j] && g[j][i])
setUnion(find(i), find(j));
for (int i = 0; i < n; i++)
{
find(i);//保证ufs[i]就是树根的坐标
if (ufs[i] < 0) ans.push_back({ i });//每个树根作为联通分量的第一个元素
}
for(int i=0;i<n;i++)
for(int j=0;j<ans.size();j++)
if (ans[j][0] == ufs[i])
{
ans[j].push_back(i);
break;
}
if (kase) cout << endl;
printf("Calling circles for data set %d:\n", ++kase);
for (int i = 0; i < ans.size(); i++)
{
for (int j = 0; j < ans[i].size(); j++)
{
if (j) cout << ", ";
cout << rev[ans[i][j]];
}
cout << endl;
}
}
return 0;
}