2167. 移除所有载有违禁货物车厢所需的最少时间
考点
- 状态机DP
- 前后缀分解
思路
一、问题抽象
给定一个长度为 \(n\) 的二进制字符串 \(s\),其中:
'1'表示该车包含非法货物;'0'表示合法车辆。
允许以下两种操作:
- 端点删除:从字符串的左端或右端删除一辆车,代价为 \(1\);
- 单点删除:删除任意一辆
'1',代价为 \(2\)。
目标是:以最小总代价移除所有
'1'(可以保留 '0')。
二、操作的结构性观察
任意一种合法方案,最终都可以视为以下三部分的组合:
- 从左端删除若干辆车;
- 从右端删除若干辆车;
- 对剩余区间中的
'1'使用单点删除。
端点删除的效果具有“区间收缩”的性质,而单点删除只作用于
'1'。 这使得问题具有明显的
动态规划结构。
三、二维状态机 DP(核心方法)
1. 状态定义
令 \(f[i][k]\) 表示处理完前 \(i\) 个字符后的最小代价,其中:
- \(f[i][0]\):前 \(i\) 个字符 全部从左端删除;
- \(f[i][1]\):前 \(i\) 个字符中 所有
'1'已被清除,允许保留'0'; - \(f[i][2]\):已经完成“前缀清除”,并开始用 右端删除的方式继续处理。
初始条件: \[ f[0][0] = 0,\quad f[0][1] = 0,\quad f[0][2] = +\infty \]
2. 状态转移
(1)状态 0:全部左端删除
\[ f[i][0] = f[i-1][0] + 1 \]
(2)状态 1:清除所有 '1'
- 若 \(s[i-1] = '0'\),不需要额外操作:
\[ f[i][1] = f[i-1][1] \]
- 若 \(s[i-1] =
'1'\),有两种选择:
- 从左端删除该字符:代价 \(f[i-1][0] + 1\)
- 单点删除该
'1':代价 \(f[i-1][1] + 2\)
取最小值: \[ f[i][1] = \min\big(f[i-1][0] + 1,\; f[i-1][1] + 2\big) \]
(3)状态 2:开始右端删除
一旦进入状态 2,后续只能通过端点删除推进: \[ f[i][2] = \min\big(f[i-1][1],\; f[i-1][2]\big) + 1 \]
3. 最终答案
\[ \text{ans} = \min\big(f[n][0],\; f[n][1],\; f[n][2]\big) \]
4. AC代码
1 | class Solution { |
滚动数组版:
1 | class Solution { |
四、前后缀分解(等价但更简洁)
1. 前缀定义
令 \(l[i]\) 表示:清除前
\(i\) 个字符中所有 '1'
的最小代价。 \[
l[0] = 0
\] 递推: \[
l[i] =
\begin{cases}
\min(i,\; l[i-1]) & s[i-1] = '0' \\
\min(i,\; l[i-1] + 2) & s[i-1] = '1'
\end{cases}
\] 其中:
- \(i\):表示直接把前缀整体删除;
- \(l[i-1] + 2\):表示单点删除该
'1'。
2. 后缀定义
令 \(r[i]\) 表示:清除区间
\([i, n]\) 中所有 '1'
的最小代价。 \[
r[n+1] = 0
\] 递推: \[
r[i] =
\begin{cases}
\min(n-i+1,\; r[i+1]) & s[i-1] = '0' \\
\min(n-i+1,\; r[i+1] + 2) & s[i-1] = '1'
\end{cases}
\]
3. 切分点合并
枚举切分点 \(i\),前缀用 \(l[i]\),后缀用 \(r[i+1]\): \[ \text{ans} = \min_{0 \le i \le n} \big( l[i] + r[i+1] \big) \]
4. AC代码
1 | class Solution { |
五、两种方法的等价性说明
- 二维状态机中的 \(f[i][1]\),本质等价于前缀数组 \(l[i]\);
- 状态 \(f[i][2]\) 的转移,等价于枚举一个切分点,将后缀整体通过端点删除;
- 因此,状态机 DP 与前后缀分解在数学上完全等价,只是实现方式不同。