2024 ICPC Asia Taichung Regional Contest C. Cube

原题链接

题意

给定一个 \(n \times n \times n\) 的立方体,

从里面选取 \(n\) 个数, 要求两两不在同一平面内, 所以总共会有 \(n\) 个数被选.

求能够选取的最小值.

其中 \(2 \le n \le 12,\ 0 \le val_{x,y,z} \le 2 \times 10^7\).

算法

动态规划, 状态压缩.

思路

题目要求求能够选取的最小值, 考虑动态规划 / 贪心.

模拟几组样例可以发现, 贪心行不通, 只得使用动态规划.

又观察到 \(2 \le n \le 12\), 可以考虑利用状态压缩进行转移.

\(f_{s_x,s_y}\) 表示 \(s_x, s_y\) 这两个 \(\{0,1\}\) 集合所表示的坐标的方格已经被选了(1 表示被选).

那么可以得到以下转移方程:

\[f_{s_x,s_y} = \min(f_{s_x \oplus (2^i),s_y \oplus (2^j)})+val_{popcount(s_x)-1,i,j} \]

其中当且仅当 \(popcount(s_x)=popcount(s_y)\) (即两集合中 1 的个数相等)且 \(s_x\) 的第 \(i\) 位为 1 以及 \(s_y\) 的第 \(j\) 位为 1 才能转移.

转移前记得将数组赋值成极大值.

#pragma GCC optimize("Ofast")

#include "iostream"

using namespace std;

constexpr int N = 13, M = 1 << 12;

int n, a[N][N][N];
int f[M][M];

inline void init()
{
    cin >> n;
    for (int i = 0; i ^ n; ++i)
        for (int j = 0; j ^ n; ++j)
            for (int k = 0; k ^ n; ++k)
                cin >> a[i][j][k];
    return;
}

inline void calculate()
{
    for (int i = 1; i ^ (1 << n); ++i)
        for (int j = 1; j ^ (1 << n); ++j)
        {
            f[i][j] = 1e9;
            int num = __builtin_popcount(i);
            if (num ^ __builtin_popcount(j)) [[likely]]
                continue;
            for (int k = 0; k ^ n; ++k)
            {
                if (!(i >> k & 1)) [[likely]]
                    continue;
                for (int l = 0; l ^ n; ++l)
                    if (j >> l & 1) [[unlikely]]
                        f[i][j] = min(f[i][j], f[i ^ (1 << k)][j ^ (1 << l)] + a[num - 1][k][l]);
            }
        }
    cout << f[(1 << n) - 1][(1 << n) - 1] << '\n';
    return;
}

inline void solve()
{
    init();
    calculate();
    return;
}

int main()
{
    cin.tie(nullptr)->ios::sync_with_stdio(false);
    solve();
    return 0;
}
posted @ 2024-11-25 19:08  Steven1013  阅读(39)  评论(0)    收藏  举报