状态压缩搜索解法(DFS + Dominance)

一、问题回顾

给定一个 M × N 的地图:

  • 0:障碍,不可通过
  • -1:加油站,可将油量重置为 100
  • 正整数:通过该格子的油耗

车辆从左上角 (0,0) 出发,到右下角 (M-1,N-1) 结束,可上下左右移动。

要求计算 保证能到达终点的最小初始油量 ,若无法到达,返回 -1


二、核心观察(关键建模)

1️⃣ 初始油量的真正含义

由于:

  • 加油站会 直接重置油量为 100
  • 油箱最大容量固定为 100

因此,初始油量只需要覆盖"最坏的一段连续消耗"

换句话说:

最小初始油量 = 路径上所有"连续消耗段"的最大值

而不是路径总油耗。


2️⃣ 路径可以拆分为若干"连续消耗段"

  • 遇到 -1:连续段结束,油量重置
  • 正整数:加入当前连续消耗段

问题转化为:

在所有可行路径中,最小化
max(每一段连续消耗)


三、状态设计

我们在 DFS 中维护如下状态:

arduino 复制代码
(y, x, min, used)

含义:

  • (y, x):当前位置
  • used:当前连续消耗段的累计油耗
  • min:到目前为止,路径上出现的 最大连续消耗段(候选答案)

终点处的答案即为:

arduino 复制代码
min === -1 ? used : min

(处理"全程无加油站"的情况)


四、剪枝与状态压缩(核心优化)

1️⃣ 基本可行性剪枝

arduino 复制代码
if (used > 100 || min > 100) return;
if (min > res) return;
  • 超过油箱上限,路径必死
  • 已经不可能优于当前最优解,直接剪枝

2️⃣ Dominance(支配)剪枝 ------ 关键创新点

对每个 (y,x),我们只保留不被支配的状态

定义 dominance 关系:

若存在历史状态 (bMin, bUsed),使得
bMin ≤ minbUsed ≤ used

则当前状态 必然更差,可安全剪枝

实现方式:

ini 复制代码
const [bMin, bUsed] = best[y][x];
if (min !== -1 && min > bMin || min === -1 && used > bMin) return;
if (min === bMin && used >= bUsed) return;
best[y][x] = [min === -1 ? bMin : min, used];

为什么这是安全的?

  • min 是最终优化目标(越小越好)
  • used 只影响后续可行性(越小越好)
  • 被支配状态 在未来不可能产生更优结果

这相当于只保留 Pareto 前沿


五、完整算法流程

  1. (0,0) 开始 DFS
  2. 维护 (min, used) 状态
  3. 遇到加油站重置 used
  4. 使用 dominance 剪枝压缩状态空间
  5. 到达终点更新答案

六、时间复杂度分析

状态规模对比

方法 每个格子状态数 总状态数
Dijkstra(fuel 维度) O(100) O(M·N·100)
本解法(Pareto 前沿) O(1)(极少) O(M·N)

总复杂度

scss 复制代码
O(M × N)

M,N ≤ 200 的限制下,对 JavaScript 非常友好


七、与 Dijkstra 解法的对比

维度 Dijkstra 本解法
状态建模 (x,y,fuel) (x,y,min,used)
状态数 Θ(M·N·100) Θ(M·N)
数据结构 优先队列 DFS + 数组
JS 性能 中等 优秀
通用性 题目特化

本解法利用了"油箱上限 + 加油站重置"的结构性约束,

在该问题下 状态空间严格小于 Dijkstra


八、完整代码(DFS + Dominance)

ini 复制代码
const rl = require("readline").createInterface({ input: process.stdin });
let lines = [];
rl.on("line", l => lines.push(l)).on("close", () => {
    const [M, N] = lines[0].split(',').map(Number);
    let grid = lines.slice(1).map(r => r.split(',').map(Number));

    let res = Infinity;
    let vis = Array.from({ length: M }, () => Array(N).fill(false));
    let best = Array.from({ length: M }, () =>
        Array.from({ length: N }, () => [Infinity, Infinity])
    );

    const dir = [[1,0],[0,1],[0,-1],[-1,0]];

    function dfs(y, x, min, used) {
        if (used > 100 || min > 100 || min > res) return;

        const [bMin, bUsed] = best[y][x];
        if (min !== -1 && min > bMin || min === -1 && used > bMin) return;
        if (min === bMin && used >= bUsed) return;

        best[y][x] = [min === -1 ? bMin : min, used];

        if (y === M - 1 && x === N - 1) {
            res = Math.min(res, min === -1 ? used : min);
            return;
        }

        for (let [dy, dx] of dir) {
            let ny = y + dy, nx = x + dx;
            if (ny < 0 || ny >= M || nx < 0 || nx >= N) continue;
            if (vis[ny][nx] || grid[ny][nx] === 0) continue;

            vis[ny][nx] = true;
            if (grid[ny][nx] === -1) {
                dfs(ny, nx, min === -1 ? used : min, 0);
            } else {
                dfs(ny, nx, min, used + grid[ny][nx]);
            }
            vis[ny][nx] = false;
        }
    }

    vis[0][0] = true;
    dfs(0, 0, -1, Math.max(grid[0][0], 0));

    console.log(res === Infinity ? -1 : res);
});

九、总结

  • 这是一次 问题特化下的状态压缩优化
  • 利用 Pareto dominance 将搜索空间压缩到极限
  • 在 JS 环境下 性能严格优于通用 Dijkstra
相关推荐
2401_841495641 分钟前
【操作系统】进程同步与互斥实验报告
c++·算法·操作系统·进程·并发·同步·互斥
爱刷碗的苏泓舒4 小时前
PPP-AR 中的参考星选取:数学原理、评价指标与切换处理
算法·gnss·模糊度固定·ppp-ar·星间单差·参考星·卫星端偏差
烬羽7 小时前
递归老写崩?一个"退回"公式,把回溯题变成填空题
javascript·深度学习·算法
闪电悠米7 小时前
力扣hot100-41.缺失的第一个正数-原地哈希详解
数据结构·算法·哈希算法
星空露珠8 小时前
28种颜色对应名称,
开发语言·数据库·算法·游戏·lua
QXWZ_IA9 小时前
桥梁数字孪生怎么落地?
人工智能·科技·算法·智能硬件·政务
Kel9 小时前
输出层与反分词(Output Layer & Detokenization)
人工智能·算法·架构
陕西企来客9 小时前
2026年7月技术好GEO优化方案:算法适配与内容策略
人工智能·算法·机器学习·技术好geo优化
风栖柳白杨10 小时前
【面试】AI算法工程师_空白自测版本
人工智能·算法·面试
闪电悠米11 小时前
力扣hot100-73.矩阵置零-标记数组详解
算法·leetcode·矩阵