1)题目意思
有 N 个宝箱(1..N),有 M 把钥匙。第 i 把钥匙价格 ai,能打开一组宝箱(给出列表)。你可以买任意多把钥匙(每把最多买一次即可,买了能重复使用,不需要买多次)。
目标:用买到的钥匙覆盖所有宝箱(每个宝箱至少能被某把已买钥匙打开),求最小总费用;如果做不到输出 -1。
范围:N<=12,M<=1000,适合状态压缩 DP。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2)思路解析(状态压缩最短路 / 0-1选择DP)
用二进制 mask 表示已经能打开的宝箱集合:
* mask 的第 j 位为 1,表示宝箱 j+1 已经能打开
* 目标 FULL = (1<<N)-1
把每把钥匙转成一个 keyMask(它能打开哪些宝箱的集合)。
定义:
* dp[mask]:达到集合 mask 的最小费用
初始化:
* dp[0]=0,其它为无穷大
转移:
* 枚举每一把钥匙 keyMask 和费用 a
* 对所有 mask:
* nmask = mask | keyMask
* dp[nmask] = min(dp[nmask], dp[mask] + a)
因为每把钥匙买不买是 0/1(买一次足够),所以这是典型的 0/1 背包式转移;为了不让同一把钥匙被重复使用,枚举 mask 时用任意顺序都可以(这里用全量枚举 + 使用 ndp 或者倒序都行)。我用更稳的 ndp 写法,避免顺序讨论。
答案:
* 若 dp[FULL] 仍是无穷大,输出 -1
* 否则输出 dp[FULL]
复杂度:
* 状态数最多 2^12=4096
* 转移 M * 2^N,约 1000*4096=400万,能过。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3)代码