题目
如果两个人互相打电话(直接或间接),则说他们在同一个电话圈里。例如,a打给b,b打给c,c打给d,d打给a,则这四个人在同一个电话圈里;如果e打给f但f不打给e,则不能推出e和f在同一个电话圈里。输入n(n≤25)个人的m次电话,找出所有的电话圈。人名只包含字母,不超过25个字符,且不重复。
思路
传统的最短路中,d[i][j]表示i到j的最短路。本题只要将d[i][j]改成i到j是否连通即可,这叫做求图的传递闭包,由于是多源的,这里用floyd。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <map>
#include <utility>
#define _for(i,a,b) for(int i = a; i<b; i++)
#define _rep(i,a,b) for(int i = a; i<=b; i++)
using namespace std;
const int maxn = 25 + 5;
map<string, int> strs;
string mystrset[maxn];
int n, m, c[maxn]; // c:ÅжÏÁ¬Í¨
bool d[maxn][maxn];
int main() {
//freopen("output.txt", "w", stdout);
int kase = 0;
while (scanf("%d%d", &n, &m) == 2 && n) {
string u, v;
memset(d, 0, sizeof(d));
strs.clear();
_for(i, 0, m) {
cin >> u >> v;
if (strs.count(u) == 0) {
mystrset[strs.size()] = u;
strs.insert(make_pair(u, strs.size()));
}
if (strs.count(v) == 0) {
mystrset[strs.size()] = v;
strs.insert(make_pair(v, strs.size()));
}
d[strs[u]][strs[v]] = true;
}
_for(i, 0, n) d[i][i] = true;
_for(k, 0, n)
_for(i, 0, n)
_for(j, 0, n)
if (d[i][k] && d[k][j])
d[i][j] = true;
memset(c, 0, sizeof(c));
int cnt = 0;
_for(i, 0, n)
if (!c[i]) {
cnt++;
_for(j, 0, n) if (d[i][j] && d[j][i]) c[j] = cnt;
}
printf("Calling circles for data set %d:\n",++kase);
if (m == 0) {
printf("\n\n");
continue;
}
_rep(k, 1, cnt) {
bool first = true;
_for(i, 0, n) if (c[i] == k) {
if (first) { cout << mystrset[i]; first = false; }
else cout << ", " << mystrset[i];
}
printf("\n");
}
printf("\n");
}
return 0;
}