Uva 247 - Calling Circles(传递闭包 / 强连通分量)
题目链接 https://vjudge.net/problem/UVA-247
【题意】
如果两个人直接或间接互相打电话,则说明他们在同一个电话圈里。例如a打给b,b打给c,c打给d,d打给a,则这4个人在同一个圈里;如果e打给f,但是f不能打回给e,那么e,f就不在一个电话圈里,输入n(n<=25)个人和m次电话,找出所有的电话圈,人名是小写字母且不超过25个字符,不会有重名的情况。
【思路】
两种思路,一种是按照紫书上讲的floyd算法求传递闭包,最后用一个并查集把在同一个圈子中的人记录下来并输出。另一种思路是可以求强连通分量,每个强连通分量中的人都在同一个圈子中。
代码一:floyd求传递闭包
#include<bits/stdc++.h>
using namespace std;
const int maxn = 30;
int n, m, cnt;
char s1[maxn], s2[maxn];
char name[maxn][maxn];
int d[maxn][maxn];
int par[maxn];
void init() {
cnt = 0;
memset(name, 0, sizeof(name));
memset(d, 0, sizeof(d));
for (int i = 0; i < maxn; ++i) par[i] = i;
}
int find(int x) {
if (x == par[x]) return x;
else return par[x] = find(par[x]);
}
void floyd() {//求传递闭包,d[i][j]==1表示i能传递到j
for (int k = 1; k <= n; ++k) {
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
d[i][j] = d[i][j] || (d[i][k] && d[k][j]);
}
}
}
}
int main() {
int kase = 0;
while (scanf("%d%d", &n, &m) == 2) {
if (!n && !m) break;
if (kase) printf("\n");
init();
for (int i = 1; i <= m; ++i) {
scanf("%s%s", s1, s2);
int u = 0, v = 0;
for (int i = 1; i <= cnt; ++i) {
if (!strcmp(s1, name[i])) u = i;
if (!strcmp(s2, name[i])) v = i;
}
if (0 == u) { u = ++cnt; strcpy(name[u], s1); }
if (0 == v) { v = ++cnt; strcpy(name[v], s2); }
d[u][v] = 1;
}
floyd();
//用并查集把不同圈子的人分到不同的集合中去
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
if (d[i][j] && d[j][i]) {
int x = find(i);
int y = find(j);
if (x != y) par[x] = y;
}
}
}
printf("Calling circles for data set %d:\n", ++kase);
for (int i = 1; i <= n; ++i) {
if (i != par[i]) continue;
printf("%s", name[i]);
for (int j = 1; j <= n; ++j) {
if (par[j] == i && j != i) printf(", %s", name[j]);
}
printf("\n");
}
}
return 0;
}
代码二:Tarjan算法求强连通分量
#include<bits/stdc++.h>
using namespace std;
const int maxn = 30;
int n, m;
int dfs_clock, scc_cnt;
int pre[maxn], lowlink[maxn], sccno[maxn];
vector<int> g[maxn], scc[maxn];
stack<int> s;
void dfs(int u) {
pre[u] = lowlink[u] = ++dfs_clock;
s.push(u);
for (int i = 0; i < g[u].size(); ++i) {
int v = g[u][i];
if (0 == pre[v]) {
dfs(v);
lowlink[u] = min(lowlink[u], lowlink[v]);
}
else if (0 == sccno[v]) {
lowlink[u] = min(lowlink[u], pre[v]);
}
}
if (lowlink[u] == pre[u]) {
++scc_cnt;
scc[scc_cnt].clear();
while (1) {
int x = s.top();
s.pop();
sccno[x] = scc_cnt;
scc[scc_cnt].push_back(x);
if (x == u) break;
}
}
}
void find_scc(int n) {
dfs_clock = scc_cnt = 0;
memset(pre, 0, sizeof(pre));
memset(sccno, 0, sizeof(sccno));
for (int i = 0; i < n; ++i) {
if (0 == pre[i]) dfs(i);
}
}
int cnt;
char s1[maxn], s2[maxn];
char name[maxn][maxn];
int main() {
int kase = 0;
while (scanf("%d%d", &n, &m) == 2) {
if (!n && !m) break;
if (kase) printf("\n");
cnt = 0;
for (int i = 0; i < maxn; ++i) g[i].clear();
for (int i = 0; i < m; ++i) {
scanf("%s%s", s1, s2);
int u = -1, v = -1;
for (int i = 0; i < cnt; ++i) {
if (!strcmp(name[i], s1)) u = i;
if (!strcmp(name[i], s2)) v = i;
}
if (-1 == u) { u = cnt; strcpy(name[cnt++], s1); }
if (-1 == v) { v = cnt; strcpy(name[cnt++], s2); }
g[u].push_back(v);
}
find_scc(n);
printf("Calling circles for data set %d:\n", ++kase);
for (int i = 1; i <= scc_cnt; ++i) {
printf("%s", name[scc[i][0]]);
for (int j = 1; j < scc[i].size(); ++j) {
printf(", %s", name[scc[i][j]]);
}
printf("\n");
}
}
return 0;
}