最短跳跃步数问题(C++实现)(奇怪的电梯)

最短跳跃步数问题(C++实现)

这个问题要求我们找到从起点 a 到终点 b 的最少跳跃次数,每次可以向左或向右跳跃 to[now] 的距离。下面我将提供几种不同的解决方案,并分析它们的优缺点。

1. BFS 解法(推荐)

广度优先搜索(BFS)天然适合求解最短路径问题,因为它会逐层扩展搜索。

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;

const int N = 2e4 + 10;
int to[N], vis[N];
int n, a, b;

int bfs() {
    if (a == b) return 0;
    
    queue<pair<int, int>> q; // {当前位置, 当前步数}
    q.push({a, 0});
    vis[a] = 1;
    
    while (!q.empty()) {
        auto [now, step] = q.front();
        q.pop();
        
        // 尝试向右跳
        int right = now + to[now];
        if (right <= n && !vis[right]) {
            if (right == b) return step + 1;
            vis[right] = 1;
            q.push({right, step + 1});
        }
        
        // 尝试向左跳
        int left = now - to[now];
        if (left >= 1 && !vis[left]) {
            if (left == b) return step + 1;
            vis[left] = 1;
            q.push({left, step + 1});
        }
    }
    
    return -1;
}

int main() {
    cin >> n >> a >> b;
    for (int i = 1; i <= n; ++i) cin >> to[i];
    
    memset(vis, 0, sizeof(vis));
    int res = bfs();
    
    cout << res << endl;
    return 0;
}

时间复杂度:O(n),每个节点最多被访问一次
空间复杂度:O(n),用于队列和访问标记

2. DFS 解法(带剪枝)

深度优先搜索也可以解决,但需要剪枝来优化。

#include <iostream>
#include <climits>
#include <cstring>
using namespace std;

const int N = 2e4 + 10;
int to[N], vis[N];
int n, a, b, ans = INT_MAX;

void dfs(int now, int step) {
    if (now == b) {
        ans = min(ans, step);
        return;
    }
    if (step >= ans) return; // 剪枝
    
    vis[now] = 1;
    
    // 向右跳
    int right = now + to[now];
    if (right <= n && !vis[right]) {
        dfs(right, step + 1);
    }
    
    // 向左跳
    int left = now - to[now];
    if (left >= 1 && !vis[left]) {
        dfs(left, step + 1);
    }
    
    vis[now] = 0; // 回溯
}

int main() {
    cin >> n >> a >> b;
    for (int i = 1; i <= n; ++i) cin >> to[i];
    
    memset(vis, 0, sizeof(vis));
    dfs(a, 0);
    
    cout << (ans == INT_MAX ? -1 : ans) << endl;
    return 0;
}

时间复杂度:最坏情况O(2^n),但剪枝后实际运行会快很多
空间复杂度:O(n),递归栈深度

3. 动态规划解法

这个问题也可以使用动态规划来解决,但实现起来不如BFS直观。

#include <iostream>
#include <vector>
#include <climits>
using namespace std;

const int N = 2e4 + 10;

int main() {
    int n, a, b;
    cin >> n >> a >> b;
    
    vector<int> to(n + 1);
    for (int i = 1; i <= n; ++i) cin >> to[i];
    
    vector<int> dp(n + 1, INT_MAX);
    dp[a] = 0;
    
    bool updated = true;
    for (int k = 0; k < n && updated; ++k) {
        updated = false;
        vector<int> temp = dp;
        
        for (int i = 1; i <= n; ++i) {
            if (dp[i] != INT_MAX) {
                // 向右跳
                int right = i + to[i];
                if (right <= n && dp[i] + 1 < temp[right]) {
                    temp[right] = dp[i] + 1;
                    updated = true;
                }
                
                // 向左跳
                int left = i - to[i];
                if (left >= 1 && dp[i] + 1 < temp[left]) {
                    temp[left] = dp[i] + 1;
                    updated = true;
                }
            }
        }
        
        dp = temp;
    }
    
    cout << (dp[b] == INT_MAX ? -1 : dp[b]) << endl;
    return 0;
}

时间复杂度:O(n^2)
空间复杂度:O(n)

算法比较

方法时间复杂度空间复杂度适用场景
BFSO(n)O(n)最优解,推荐使用
DFS最坏O(2^n)O(n)小规模数据,需要剪枝
DPO(n^2)O(n)理论可行,但不如BFS高效

输入输出示例

输入1:

5 1 5
3 3 1 2 5

输出1:

3

解释: 1 → 4 → 2 → 5

输入2:

5 1 2
1 2 1 2 1

输出2:

1

解释: 可以直接从1跳到2

输入3:

3 1 3
1 1 1

输出3:

-1

解释: 无法到达终点3

总结

对于这个问题(最短跳跃步数问题),BFS是最优的解决方案,因为它能保证在O(n)时间内找到最短路径。DFS虽然可行,但在最坏情况下性能较差。动态规划的实现相对复杂,且时间复杂度较高。在实际编程竞赛中,推荐使用BFS来解决这类最短路径问题。

栗子: 奇怪的电梯问题🦉🦉

问题描述

假设有一个奇怪的电梯,共有N层(1~N)。给定:
当前所在楼层A
目标楼层B
每层楼有一个数字K_i,表示可以从该层上移K_i层或下移K_i层(不能超出楼层范围)
要求计算从A到B的最少按键次数(即最少移动次数),如果无法到达则返回-1。

BFS解法(推荐)

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;

const int MAXN = 205;  // 假设最大楼层数

int K[MAXN];          // 每层的移动数字
int vis[MAXN];        // 访问标记数组
int N, A, B;          // 总楼层数, 起始层, 目标层

int bfs() {
    if (A == B) return 0;  // 已经在目标层
    
    queue<pair<int, int>> q;  // {当前楼层, 按键次数}
    q.push({A, 0});
    vis[A] = 1;
    
    while (!q.empty()) {
        auto [current, steps] = q.front();
        q.pop();
        
        // 向上移动
        int up = current + K[current];
        if (up <= N && !vis[up]) {
            if (up == B) return steps + 1;
            vis[up] = 1;
            q.push({up, steps + 1});
        }
        
        // 向下移动
        int down = current - K[current];
        if (down >= 1 && !vis[down]) {
            if (down == B) return steps + 1;
            vis[down] = 1;
            q.push({down, steps + 1});
        }
    }
    
    return -1;  // 无法到达
}

int main() {
    cin >> N >> A >> B;
    for (int i = 1; i <= N; ++i) {
        cin >> K[i];
    }
    
    memset(vis, 0, sizeof(vis));
    int result = bfs();
    
    cout << result << endl;
    return 0;
}

代码解析

  1. 初始化

    • 定义最大楼层数MAXN
    • K数组存储每层的移动数字
    • vis数组记录已访问的楼层
  2. BFS函数

    • 使用队列存储待处理的楼层和当前步数
    • 每次处理队列头部元素,尝试向上/向下移动
    • 如果到达目标楼层,立即返回当前步数
    • 如果队列处理完毕仍未找到目标,返回-1
  3. 主函数

    • 读取输入数据
    • 初始化访问数组
    • 调用BFS并输出结果

DFS解法(带剪枝)

#include <iostream>
#include <climits>
using namespace std;

const int MAXN = 205;
int K[MAXN], vis[MAXN];
int N, A, B, min_steps = INT_MAX;

void dfs(int current, int steps) {
    if (current == B) {
        min_steps = min(min_steps, steps);
        return;
    }
    if (steps >= min_steps) return;  // 剪枝
    
    vis[current] = 1;
    
    // 向上移动
    int up = current + K[current];
    if (up <= N && !vis[up]) {
        dfs(up, steps + 1);
    }
    
    // 向下移动
    int down = current - K[current];
    if (down >= 1 && !vis[down]) {
        dfs(down, steps + 1);
    }
    
    vis[current] = 0;  // 回溯
}

int main() {
    cin >> N >> A >> B;
    for (int i = 1; i <= N; ++i) {
        cin >> K[i];
    }
    
    dfs(A, 0);
    
    cout << (min_steps == INT_MAX ? -1 : min_steps) << endl;
    return 0;
}

算法比较

方法时间复杂度空间复杂度适用场景
BFSO(N)O(N)最优解,推荐使用
DFS最坏O(2^N)O(N)小规模数据,需要剪枝

输入输出示例

输入1:

5 1 5
3 3 1 2 5

输出1:

3

解释: 1 → 4 → 2 → 5

输入2:

5 1 2
1 2 1 2 1

输出2:

1

解释: 可以直接从1跳到2

输入3:

3 1 3
1 1 1

输出3:

-1

解释: 无法到达目标楼层3

优化建议

  1. 双向BFS:可以同时从起点和终点开始搜索,当两个搜索相遇时停止,可以进一步提高效率。

  2. 预处理:如果问题规模特别大,可以考虑预处理楼层间的可达关系。

  3. 记忆化搜索:DFS可以结合记忆化技术,避免重复计算。

奇怪的电梯问题优化建议

总结

对于"奇怪的电梯"这类最短路径问题,BFS是最佳选择,因为它能保证找到最短路径且时间复杂度较低。DFS虽然实现简单,但在最坏情况下性能较差。在实际应用中,建议优先使用BFS解法。🐍🐍🐍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值