Skip to content

题解

很不幸,这个问题是 NP-hard 的。本题所对应的判定问题是“给定一个数 \(k\) ,判断是否存在割值至少为 \(k\) 的划分”,这个则是 NP-完全的。

对于 NP-hard 问题,我们不指望找到它们的多项式时间算法(即用时为 \(\mathcal{O}(n^{a})\) 的算法,其中 \(a\) 可以是任意大的正数)。

如果要查阅更多资料,可以搜索关键词“最大割”和“加权最大割”。本题就是加权最大割问题。

由于数据量比较小,我们直接采用暴力算法,枚举每一种可能的划分即可。所有可能的划分数有 \(2^{n}\) 种,而计算一个划分的权重之和最多需要 \(\mathcal{O}(n^{2})\) 时间,因此总用时不会超过 \(\mathcal{O}(n^{2} 2^{n})\) 。

完整代码

C:

#include <stdio.h>
#include <stdint.h>

int w[20][20];

int main(void)
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            scanf("%d", &w[i][j]);
        }
    }

    // 因为下方做了将计算量减半的优化,所以 n == 0 要特判。
    if (n == 0) {
        printf("%d\n", 0);
    }

    int best_weight_sum = 0;
    // 其实只需要枚举前 2^{n-1} 种情况,
    // 因为后 2^{n-1} 种情况和前面的一一对应地重复了。
    const uint32_t all_bits = (1 << n) - 1;
    for (uint32_t a_bits = 0; a_bits < (1u << (n - 1)); ++a_bits) {
        const uint32_t b_bits = all_bits & ~a_bits;

        int weight_sum = 0;
        // 计算这一划分下的权重之和
        for (int va = 0; va < n; ++va) {
            for (int vb = 0; vb < n; ++vb) {
                if (!(
                    ((a_bits >> va) & 1)
                    && ((b_bits >> vb) & 1)))
                {
                    // 未满足“va 在集合 A 中,vb 在集合 B 中”这一条件
                    continue;
                }
                weight_sum += w[va][vb];
            }
        }
        // 这一划分下的权重之和是一个新的最优结果,所以更新一下最优权重之和
        if (weight_sum > best_weight_sum) {
            best_weight_sum = weight_sum;
        }
    }

    printf("%d\n", best_weight_sum);
}

C++:

#include <algorithm>
#include <array>
#include <cstdint>
#include <iostream>

std::array<std::array<int, 20>, 20> w{};

int main()
{
    int n;
    std::cin >> n;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            std::cin >> w[i][j];
        }
    }

    // 因为下方做了将计算量减半的优化,所以 n == 0 要特判。
    if (n == 0) {
        std::cout << 0 << '\n';
    }

    int best_weight_sum = 0;
    // 其实只需要枚举前 2^{n-1} 种情况,
    // 因为后 2^{n-1} 种情况和前面的一一对应地重复了。
    const std::uint32_t all_bits = (1 << n) - 1;
    for (std::uint32_t a_bits = 0; a_bits < (1u << (n - 1)); ++a_bits) {
        const std::uint32_t b_bits = all_bits & ~a_bits;

        int weight_sum = 0;
        // 计算这一划分下的权重之和
        for (int va = 0; va < n; ++va) {
            for (int vb = 0; vb < n; ++vb) {
                if (!(
                    ((a_bits >> va) & 1)
                    && ((b_bits >> vb) & 1)))
                {
                    // 未满足“va 在集合 A 中,vb 在集合 B 中”这一条件
                    continue;
                }
                weight_sum += w[va][vb];
            }
        }
        // 更新最优权重和
        best_weight_sum = std::max(best_weight_sum, weight_sum);
    }

    std::cout << best_weight_sum << '\n';
}