1 条题解

  • 0
    @ 2025-10-28 15:26:37

    「ICPC World Finals 2025」出故障的漫游车 题解

    问题分析

    我们需要确定漫游车在执行给定移动序列时,方向顺序被宇宙射线改变的最少次数。

    关键约束

    • 方向顺序是 N、E、S、W 的一个排列(共 4!=244! = 24 种)
    • 每次移动时,漫游车按当前方向顺序选择第一个可行的方向
    • 宇宙射线可能在任何两次移动之间改变方向顺序

    算法思路:动态规划

    状态定义

    dp[i][j] 表示执行完前 ii 步移动,且当前方向顺序为第 jj 种排列时的最小改变次数。

    状态转移

    对于第 ii 步移动:

    • 如果方向顺序 jj 能够产生正确的移动,则可以从 dp[i-1][j] 转移(方向顺序不变)
    • 也可以从任意 dp[i-1][k] 转移,代价增加 11(方向顺序改变)

    代码实现解析

    1. 预定义所有方向排列

    constexpr PII idtovec[24][4] = {
        {{-1, 0}, {0, -1}, {0, 1}, {1, 0}},  // 排列 0: N, W, E, S
        {{-1, 0}, {0, -1}, {1, 0}, {0, 1}},  // 排列 1: N, W, S, E
        // ... 共24种排列
    };
    

    作用:预计算所有 2424 种方向顺序对应的移动优先级。

    2. 方向字符到向量的转换

    inline PII chtovec(const char &ch) {
        if (ch == 'N') return {-1, 0};
        if (ch == 'E') return {0, 1};
        if (ch == 'S') return {1, 0};
        if (ch == 'W') return {0, -1};
        __builtin_unreachable();
    }
    

    功能:将方向字符转换为坐标偏移量。

    3. 获取下一个位置

    inline PII getnxt(const PII &cur, const PII(&dif)[4]) {
        PII res;
        for (const PII &d : dif) {
            res = {cur.first + d.first, cur.second + d.second};
            if (res.first >= 0 && res.first < n && res.second >= 0 && 
                res.second < m && mp[res.first][res.second] != '#')
                return res;  // 返回第一个可行的方向
        }
        __builtin_unreachable();
    }
    

    逻辑:按方向顺序尝试每个方向,选择第一个不会出界或撞到岩石的方向。

    4. 动态规划核心

    for (int i = 1; i <= len; ++i, o ^= 1) {
        d = chtovec(str[i]);
        d.first += pos.first, d.second += pos.second;
    
        for (int j = 0; j < 24; ++j) {
            if (getnxt(pos, idtovec[j]) != d) {
                dp[o][j] = 0x3f3f3f3f;  // 不可达
                continue;
            }
    
            dp[o][j] = dp[o ^ 1][j];  // 方向顺序不变
            
            // 考虑所有可能的前一状态
            for (int k = 0; k < 24; ++k)
                dp[o][j] = min(dp[o][j], dp[o ^ 1][k] + (j != k));
        }
        pos = d;  // 更新当前位置
    }
    

    状态转移分析

    • 检查方向顺序 jj 是否能产生正确的移动
    • 如果可行,最小代价 = min(保持原顺序, 从其他顺序改变过来)

    5. 滚动数组优化

    int o = 0;
    for (int i = 1; i <= len; ++i, o ^= 1) {
        // 使用 dp[o] 和 dp[o^1] 交替存储
    }
    

    优化目的:将空间复杂度从 O(L×24)O(L \times 24) 降为 O(24)O(24)

    复杂度分析

    • 时间复杂度O(L×242)O(L \times 24^2)
      • LL:移动序列长度(最多 1000010000
      • 24224^2:状态转移的常数
    • 空间复杂度O(24)O(24)(滚动数组)

    样例解析

    样例1:NNEN

    输入:
    5 3
    #..
    ...
    ...
    ...
    .S.
    NNEN
    
    输出:1
    

    解释:至少需要一次方向顺序改变才能解释移动序列。

    样例2:NEESNS

    输出:0
    

    解释:存在一个方向顺序(如 NESW)可以解释所有移动。

    样例3:NEESNNWWSENESS

    输出:4
    

    解释:需要至少4次方向顺序改变。

    算法优势

    1.1. 完备性:枚举所有可能的方向顺序排列

    2.2. 最优性:动态规划保证找到最小改变次数

    3.3. 高效性:利用滚动数组优化空间

    4.4. 正确性:严格模拟漫游车的移动逻辑

    该解法通过状态空间搜索动态规划,有效地解决了漫游车方向顺序变化的最优化问题。

    • 1

    「ICPC World Finals 2025」出故障的漫游车

    信息

    ID
    4501
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者