题解

个人分成两组,那么一共是种情况,如果每一次都暴力计算竞争力总和,那么每一次的时间复杂度将是,总时间复杂度是,对于这个数据范围来说是过不去的。

考虑如何惰性计算这个竞争力总和,也就是说从状态转移到状态的时候根据状态的变化来计算贡献,这是一个常用的思路。

起初,我尝试讨论第个元素是被分到哪一组,元素加入组对总和的贡献就是,加到一个变量里求和即可,渐进时间复杂度,但是可能由于姿势不当TLE了。后来在题解中看到了一种思路类似的解法——算贡献的时候不用加,用减。假设当前没有分组的变量的竞争值就是,每次将一个元素分到某一组后,这个元素和原来这个组里的所有元素的贡献需要减去,那么在全分配完之后得到的这个变量的值就是还没有被“割断”的“边”。这样做有个好处就是有一个可以减枝的条件:如果当前还没有被“割断”的“边”的总和已经小于当前的最大值了,这条路就没有必要继续枚举了,所以在同是的复杂度的情况下,这个算法快了很多,1676/4000ms。

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

const int MAX_N = 14 * 2 + 5;

int n, tot;
int v[MAX_N][MAX_N];
int a[MAX_N], aPtr, b[MAX_N], bPtr;

LL sum, maxAns = -1;

void dfs(int x, int cnt) {
    if (sum < maxAns) return;
    if (cnt > n || cnt + (tot - x + 1) < n) return;
    if (x == tot + 1) {
        maxAns = max(maxAns, sum);
        return;
    }
    LL contribute;

    contribute = 0;
    for (int i = 1; i <= aPtr; ++i) contribute -= v[x][a[i]];
    a[++aPtr] = x; sum += contribute;
    dfs(x + 1, cnt + 1);
    --aPtr; sum -= contribute;

    contribute = 0;
    for (int i = 1; i <= bPtr; ++i) contribute -= v[x][b[i]];
    b[++bPtr] = x; sum += contribute;
    dfs(x + 1, cnt);
    --bPtr; sum -= contribute;
}

int main() {
    scanf("%d", &n);
    tot = (n << 1);

    for (int i = 1; i <= tot; ++i) {
        for (int j = 1; j <= tot; ++j) {
            scanf("%d", &v[i][j]);
            sum += (i < j) * v[i][j];
        }
    }

    dfs(1, 0);

    printf("%lld\n", maxAns);

    return 0;
}

我的博客

www.cfzhao.com
(欢迎大佬们py友链啊!)