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 @   Steven1013  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示