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;
}
posted @ 2018-02-03 19:40  不想吃WA的咸鱼  阅读(265)  评论(0编辑  收藏  举报