题解:三维立方体选数最小和(X、Y、Z 都不能重复)
题目概述
给你一个 n×n×n 的立方体,每个位置 (x,y,z) 有一个权值。你要选出 n 个位置,使得:
* 任何两个被选位置的 x 不相同
* 任何两个被选位置的 y 不相同
* 任何两个被选位置的 z 不相同
在满足以上限制的前提下,让权值总和最小。
n <= 12。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
核心思路:按 X 分层 + 状态压缩 DP
因为每个 x 只能用一次,我们可以按 x=1..n 依次决定“这一层选哪个 (y,z)”。
当已经处理了前 k 个 x 时,我们已经选了 k 个点,也就用掉了:
* k 个不同的 y
* k 个不同的 z
所以用两个二进制集合表示使用情况:
* ymask:哪些 y 已经用过
* zmask:哪些 z 已经用过
并且必须满足:ymask 中 1 的个数等于 zmask 中 1 的个数(因为每选一个点就同时消耗一个 y 和一个 z)。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
状态定义
把所有 mask 按 popcount(1 的个数)分层。
在第 k 层(表示已用 k 个 y、k 个 z,等价于已经处理完 x=1..k):
* cur[iy][iz] 表示:
ymask = lst[k][iy],zmask = lst[k][iz] 时的最小总和。
其中 lst[k] 是所有 popcount(mask)=k 的 mask 列表。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
初始化
k=0 时只有一种状态:
* ymask=0,zmask=0,代价为 0
即:cur[0][0]=0
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
转移
从第 k 层转到第 k+1 层,此时要处理的 x 是:
* x = k + 1
对当前状态 (ymask, zmask):
1. 枚举一个没用过的 y
2. 枚举一个没用过的 z
3. 选择点 (x, y, z),进入新状态:
* ymask2 = ymask | (1<<(y-1))
* zmask2 = zmask | (1<<(z-1))
* 新代价 = 旧代价 + cost[x][y][z]
取最小值更新到下一层 nxt。
为了能快速从 mask 找到它在 lst[k+1] 里的位置,用 pos[mask] 做映射(O(1) 找下标)。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
结果
当 k=n 时,ymask 和 zmask 都必须是全 1(全部用过):
* 输出对应状态的最小值即可。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
复杂度
* 状态只枚举同层的有效 mask,规模大约是 C(n,k)^2。
* 每个状态转移枚举剩余的 y、z,数量最多是 (n-k)^2。
* n <= 12 时整体运算量在可接受范围内,且内存远小于直接开 dp[4096][4096],不会 MLE,也比哈希版常数小很多,通常也不会 TLE。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
参考代码