1)题目意思
给你一个 n×m 的城市网格,每个格子 (i,j) 建一个油库要花 F[i][j] 的钱。
建完后要保证每个城市满足下面之一:
* 自己有油库
* 或者与某个有油库的城市相邻(上下左右相邻)
要求输出一组最优方案的:
1. 油库个数最少(在“总代价最小”的前提下)
2. 方案总代价最小(最主要目标)
也就是:先最小化总代价,若总代价相同再最小化油库数量。
数据范围:n*m<=50 且 m<=n,因此 m 最大不会超过 7(否则 8*8>50),可以做按行状态压缩 DP。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2)思路解析(按行状态压缩 DP)
关键观察
每个格子是否被覆盖,只与它自己和上下左右有关。按“行”处理时,第 i 行能否被覆盖,取决于:
* 第 i-1 行哪些列有油库(prev 掩码)
* 第 i 行哪些列有油库(cur 掩码)
* 第 i+1 行哪些列有油库(nxt 掩码)
因为:
* 上下覆盖看同列(prev/nxt 的同一位)
* 左右覆盖看同一行相邻列(cur 的相邻位)
* 自己覆盖看 cur 的本位
状态表示
用一个二进制数 mask 表示一行哪些列建油库(m<=7,所以 mask 最大 127)。
定义 DP:
* dp[i][prev][cur] 表示:已经决定到第 i 行(也就是第 1..i 行的建库情况都确定),并且保证 第 1..i-1 行都已经被覆盖。
其中第 i-1 行的建库情况为 prev,第 i 行的建库情况为 cur。
DP 存两件事:总代价 和 油库数量(用于相同总代价时比较)。
转移
枚举下一行 nxt(第 i+1 行的建库掩码),检查第 i 行是否被覆盖:
* 如果第 i 行在 (prev,cur,nxt) 的共同作用下能全覆盖,则可以转移:
* dp[i+1][cur][nxt] = min( dp[i+1][cur][nxt], dp[i][prev][cur] + 行(i+1,nxt)的代价与数量 )
这里“min”按题意比较:
1. 代价小优先
2. 代价相同则油库少优先
初始化与收尾
* 第 0 行不存在,视作 prev=0
* 初始化:枚举第 1 行 cur,dp[1][0][cur] = 第1行cur的花费/数量
* 最后一行 n 需要被覆盖,因此在结束时令 nxt=0(第 n+1 行不存在)检查第 n 行覆盖即可。
复杂度
状态数大约是 n * 2^m * 2^m,每个状态枚举 nxt 再检查覆盖,m<=7 很稳能过。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3)代码