「POJ 1737」Connected Graph - 组合数 + 计数原理 + 递推

给定 N N \leq 50 )个点,在平面上固定其位置,求这些点最多能组成多少个不同的无向连通图。

链接

POJ 1737

题解

统计连通图的方案数是困难的,但我们可以轻易地计算出用 N 个点组成任意图的方案数:因为 N 个点的无向图最多有 \frac{N(N - 1)}{2} 条边,考虑每条边选或不选,则共有 2 ^ {\frac{N(N - 1)}{2}} 种不同的图。

求出任意图的方案数后,只要再求出非连通图的方案数,就可以得到答案。考虑 N 个点组成的非连通图中的点 v ,它一定处于一个由 K 1 \leq K \leq N - 1 )个点组成的连通分量中,点 v 确定后,组成这个连通分量还需要 K - 1 个点,总方案数为 \binom{N - 1}{K - 1} ;每个连通分量都是一个连通图,可以递归来求;考虑完一个连通分量,图的剩余部分(与该连通分量隔离的 N - K 个点)是一个任意图,也可以递归来求。

n 个点组成连通图的方案数为 f(n) 、组成非连通图的方案数为 g(n) 、组成任意图的方案数为 h(n) ,则递归计算 f(n) 的公式为:

\begin{align} & f(n) = h(n) - g(n) \\ & g(n) = \sum\limits_{k = 1}^{n - 1}\binom{n - 1}{k - 1} * f(k) * h(n - k) \\ & h(n) = 2 ^ {\frac{n(n - 1)}{2}} \\ \end{align}

需要使用高精度。

代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>

const int MAXN = 50;

struct BigInt;
BigInt &countConnectedGraphs(int n);
BigInt &countNonConnectedGraphs(int n);
inline BigInt &countAllGraphs(int n);

struct BigInt {
    std::vector<char> v;

    BigInt() {
        *this = 0;
    }

    BigInt(int x) {
        *this = x;
    }

    BigInt &operator=(int x) {
        v.clear();
        do v.push_back(x % 10); while (x /= 10);
        return *this;
    }

    BigInt &operator=(const BigInt &x) {
        v.resize(x.v.size());
        memcpy(const_cast<char *>(v.data()), x.v.data(), x.v.size() * sizeof(char));
        return *this;
    }
};

std::ostream &operator<<(std::ostream &out, const BigInt &x) {
    for (int i = x.v.size() - 1; i >= 0; i--) out << (char)(x.v[i] + '0');
    return out;
}

BigInt operator+(const BigInt &a, const BigInt &b) {
    BigInt result;
    result.v.clear();
    bool flag = false;
    for (int i = 0; i < (int)std::max(a.v.size(), b.v.size()); i++) {
        int tmp = 0;
        if (i < (int)a.v.size()) tmp += a.v[i];
        if (i < (int)b.v.size()) tmp += b.v[i];
        if (flag) tmp++, flag = false;
        if (tmp >= 10) tmp -= 10, flag = true;
        result.v.push_back(tmp);
    }
    if (flag) result.v.push_back(1);

    return result;
}

BigInt &operator+=(BigInt &a, const BigInt &b) {
    return a = a + b;
}

BigInt operator-(const BigInt &a, const BigInt &b) {
    BigInt result;
    result.v.clear();
    bool flag = false;
    for (int i = 0; i < (int)a.v.size(); i++) {
        int tmp = a.v[i];
        if (i < (int)b.v.size()) tmp -= b.v[i];
        if (flag) tmp--, flag = false;
        if (tmp < 0) tmp += 10, flag = true;
        result.v.push_back(tmp);
    }

    int size = result.v.size();
    while (size > 1 && result.v[size - 1] == 0) size--;
    result.v.resize(size);

    return result;
}

BigInt operator*(const BigInt &a, const BigInt &b) {
    BigInt result;
    result.v.resize(a.v.size() + b.v.size());
    for (int i = 0; i < (int)a.v.size(); i++) {
        for (int j = 0; j < (int)b.v.size(); j++){
            result.v[i + j] += a.v[i] * b.v[j];
            result.v[i + j + 1] += result.v[i + j] / 10;
            result.v[i + j] %= 10;
        }
    }

    int size = result.v.size();
    while (size > 1 && result.v[size - 1] == 0) size--;
    result.v.resize(size);

    return result;
}

BigInt combo[MAXN + 1][MAXN + 1];

inline void makeComboTable() {
    for (int i = 1; i <= MAXN; i++) {
        combo[i][0] = combo[i][i] = 1;
        for (int j = 1; j < i; j++){
            combo[i][j] = combo[i - 1][j] + combo[i - 1][j - 1];
        }
    }
}

inline BigInt &C(int n, int k) {
    return combo[n][k];
}

BigInt &countConnectedGraphs(int n) {
    static bool calced[MAXN + 1];
    static BigInt mem[MAXN + 1];
    BigInt &ans = mem[n];

    if (calced[n]) return ans;
    calced[n] = true;

    ans = countAllGraphs(n) - countNonConnectedGraphs(n);

    return ans;
}

BigInt &countNonConnectedGraphs(int n) {
    static bool calced[MAXN + 1];
    static BigInt mem[MAXN + 1];
    BigInt &ans = mem[n];

    if (calced[n]) return ans;
    calced[n] = true;

    for (int k = 1; k <= n - 1; k++) {
        ans += C(n - 1, k - 1) * countConnectedGraphs(k) * countAllGraphs(n - k);
    }

    return ans;
}

inline BigInt &countAllGraphs(int n) {
    static bool calced[MAXN + 1];
    static BigInt mem[MAXN + 1];
    BigInt &ans = mem[n];

    if (calced[n]) return ans;
    calced[n] = true;

    BigInt x = 2;
    int t = n * (n - 1) / 2;
    for (ans = 1; t; t >>= 1, x = x * x) {
        if (t & 1) ans = ans * x;
    }

    return ans;
}

int main() {
    makeComboTable();

    for (int n; ~scanf("%d", &n) && n > 0; ) {
        std::cout << countConnectedGraphs(n) << std::endl;
    }

    return 0;
}