
校园网:给定N所学校和网络,目标是分发软件其他学校都可收到,求①所需最少分发学校数;②若任选学校都能收到,最低新增边数。
4.3成为图论大师之路
强连通分量分解
还是睡不着,再来一发吧。同一个强连通分量内的顶点合并为一个,在这个DAG上计算出度和入度。①其实是求入度为0的顶点数,②则是求0出度和0入度顶点数的较大者,因为要将这两类顶点连起来。
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
#define MAX_V 100 + 2
int V;                      // 顶点数
vector<int> G[MAX_V];       // 图的邻接表表示
vector<int> rG[MAX_V];      // 反向图
vector<int> vs;             // 后序遍历顺序的顶点列表
bool used[MAX_V];           // 访问标记
int cmp[MAX_V];             // 所属强连通分量的拓补序
int in[MAX_V], out[MAX_V];  // 入度、出度
void add_edge(const int &from, const int &to)
{
    G[from].push_back(to);
    rG[to].push_back(from);
}
void dfs(const int &v)
{
    used[v] = true;
    for (int i = 0; i < G[v].size(); ++i)
    {
        if (!used[G[v][i]])
            dfs(G[v][i]);
    }
    vs.push_back(v);
}
void rdfs(const int &v, const int &k)
{
    used[v] = true;
    cmp[v] = k;
    for (int i = 0; i < rG[v].size(); ++i)
    {
        if (!used[rG[v][i]])
            rdfs(rG[v][i], k);
    }
}
int scc()
{
    memset(used, 0, sizeof(used));
    vs.clear();
    for (int v = 0; v < V; ++v)
    {
        if (!used[v])
            dfs(v);
    }
    memset(used, 0, sizeof(used));
    int k = 0;
    for (int i = vs.size() - 1; i >= 0; --i)
    {
        if (!used[vs[i]])
            rdfs(vs[i], k++);
    }
    return k;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif
    scanf("%d", &V);
    for (int u = 0; u < V; ++u)
    {
        int v;
        while (scanf("%d", &v) && v)
        {
            add_edge(u, --v);
        }
    }
    int n = scc();
    if (n == 1)
    {
        puts("1\n0");       // 特殊情况
        return 0;
    }
    for (int u = 0; u < V; ++u)
    {
        for (int i = 0; i < G[u].size(); ++i)
        {
            int v = G[u][i];
            if (cmp[u] != cmp[v])               // 强连通分量算一个点
                ++out[cmp[u]], ++in[cmp[v]];
        }
    }
    int zero_in = 0, zero_out = 0;
    for (int i = 0; i < n; ++i)
    {
        if (in[i] == 0)
            ++zero_in;
        if (out[i] == 0)
            ++zero_out;
    }
    printf("%d\n", zero_in);
    printf("%d\n", max(zero_in, zero_out));    // 入度为0的点和出度为0的点建立连接
#ifndef ONLINE_JUDGE
    fclose(stdin);
#endif
    return 0;
}
| 16237875 | hankcs | 1236 | Accepted | 176K | 16MS | C++ | 2038B | 2016-10-28 15:41:17 | 
 知识共享署名-非商业性使用-相同方式共享:码农场 » POJ 1236 Network of Schools 题解《挑战程序设计竞赛》
码农场