跑路

跑路

题目链接

一、题目理解与分析

1. 题意转化

最后我已经处理完了这个路径,并得到了一个距离 \(L\) ,最后我需要的就是关于 \(L\) 的拆分:

$ L = 2^{k1} + 2^{k2} + ··· + 2^{k_i} $

最后我要输出的是 \(i\) 的最小值

2. 最终思考

最后我的得到的 L 转换成二进制时,所含1的个数必须最少

二、算法设计

核心思想:倍增 + dfs

Step1:倍增预处理
vis[i][j][k] = true //这个表示从点 i 到点 j 有恰好长度为2的k次方的路径
初始状态定义:
  • 若存在边 i => j ,则定义:vis[i][j][0] = ture
Step2 状态转移:

**可能存在几个中间点 $ mid_1,mid_2,···,mid_n$ **

  • 有从 i\(mid_i\) 有一条长度为 $ 2 ^ {k-1} $ 的路径

  • 有从 \(mid_i\)j 有一条长度为 $ 2 ^ {k-1} $ 的路径

Step3 BFS的引入:

运用BFS,从1到n,每次移动约等于用一次跑路器

求到的最短路径长度处理后就等于最少秒数(最终需要最优策略)

三、代码:此处为AI填写注释,有不懂的立马询问台上讲题者/论坛内询问

#include <bits/stdc++.h>
using namespace std;

const int INF = 0x3f3f3f3f;

vector<int> mp[55];  // 邻接表存原图
int n, m;
int step[55];        // BFS距离数组
bool vis[55][55][55]; // 核心状态数组

int main() {
    // 输入
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        mp[u].push_back(v);
        vis[u][v][0] = true;  // 初始:直接相邻
    }
    
    // 第一部分:倍增预处理
    // 为什么 k 到 32?
    // maxlongint ≈ 2^31,所以 2^k 最大到 2^31,k最大31
    // 取32更安全
    for (int k = 1; k <= 32; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                // 枚举所有可能的中间点
                for (int mid = 1; mid <= n; mid++) {
                    // 如果 i->mid 和 mid->j 都有 2^{k-1} 的路径
                    // 那么 i->j 就有 2^k 的路径
                    if (vis[i][mid][k-1] && vis[mid][j][k-1]) {
                        vis[i][j][k] = true;
                    }
                }
            }
        }
    }
    
    // 第二部分:BFS求最少秒数
    memset(step, 0x3f, sizeof(step));
    step[1] = 0;
    queue<int> q;
    q.push(1);
    
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        
        // 尝试所有可能的 2^k 跳转
        for (int k = 0; k <= 32; k++) {
            for (int v = 1; v <= n; v++) {
                // 如果 u 可以 1 秒到 v,且可以更新距离
                if (vis[u][v][k] && step[v] > step[u] + 1) {
                    step[v] = step[u] + 1;
                    q.push(v);
                }
            }
        }
    }
    
    // 输出结果
    cout << step[n] << endl;
    
    return 0;
}

四、复杂度分析

PART 1 时间复杂度
1.预处理部分

三层n循环+一层32循环

总复杂度为$ O(32 \times n^3)$

2.BFS

两层n循环+一层32循环

总复杂度\(O(32\times n^2)\)

总复杂度

$ O(max(32 \times n^3,32\times n^ 2)) = O(32 \times n^3)$

题目限制条件中 $ n \le 50 $ ,完全可行

PART 2 空间复杂度

vis: $ n^2 \times 55 $ 一个M都没到,可行!

五、常见疑问

Q1:为什么k的max可以取到 $ {32} $ ,longint不是 \({31}\) 吗?

A:取到\(32\)可以理解为缓冲,不会影响最终结果

Q2:自环的作用是什么?

A:例如样例中的1=>1,重构路径长度为\(2^k\),得到最优解

Q3:为什么 BFS 中要检查所有 k?

A:从u到v可以有最多$ 2^k\(的方式到达,所以说我们知道了这个路径为\) 2^k$那么我们就可以知道这个时间是1s,与k无关,只是进行计算罢了

Q4:重边处理

A:用vis数组表示,重边也不影响结果

©2025 WJW all right reserved

posted @ 2025-12-13 15:40  YG_Luminous  阅读(5)  评论(0)    收藏  举报