当前位置:首页 > 牛客 > 牛客16445题:用Dijkstra算法解决骑车与步行最短路径问题

牛客16445题:用Dijkstra算法解决骑车与步行最短路径问题

7个月前 (09-16)

牛客16445题:用Dijkstra算法解决骑车与步行最短路径问题 Dijkstra算法 图论 最短路径 状态压缩 优先队列 C++ 有向图 第1张

一、题目解读

牛客16445题要求解决一个最短路径问题:给定一个有向图,部分节点可获取自行车,骑行时边权减半,需在两种模式下(步行/骑车)计算从起点到终点的最短距离。题目难点在于状态转换与边权动态处理。

二、解题思路

1. 状态压缩:将节点分为“步行状态”和“骑车状态”,用节点编号偏移量表示(如原节点n扩展为n和n+n两种状态)。

2. Dijkstra算法:利用优先队列维护当前最短路径状态,通过松弛操作更新子节点距离。

3. 动态边权处理:骑行时边权减半,需判断当前是否持有自行车(通过has_bike数组或状态标记)。

4. 优化剪枝:若当前状态距离已劣于已知最短距离,直接跳过,避免无效扩展。

三、解题步骤

1. 初始化图结构,读取节点数m、边数n及自行车节点标记。

2. 构建邻接表adj存储边信息(节点v, 边权w)。

3. 定义状态结构体State(距离dist、节点node、是否骑车has_bike),重载比较运算符适配优先队列

4. 执行Dijkstra算法

○ 初始状态:起点步行距离为0,入队。

○ 循环弹出队首状态,遍历邻接边:

a. 步行状态:更新普通节点距离。

b. 骑车状态:若当前可骑车(原有自行车或当前节点可获取),计算减半边权并更新扩展状态。

○ 剪枝:若当前状态已劣于已知最短距离,跳过。

5. 最终距离为终点骑车/步行状态的最小值。

四、代码和注释

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

typedef long long ll;
typedef pair<ll, int> pli;

const int MAXN = 1e5 + 5;
const ll INF = LLONG_MAX;

vector<pair<int, int>> adj[MAXN];
bool has_bike[MAXN] = {false};

struct State {
    ll dist;
    int node;
    bool has_bike;
    bool operator>(const State& other) const {
        return dist > other.dist;
    }
};

void dijkstra(int start, int n, vector<ll>& dist) {
    priority_queue<State, vector<State>, greater<State>> pq;
    dist.assign(2 * n + 2, INF);
    
    // 初始状态:步行到达起点
    dist[start] = 0;
    pq.push({0, start, has_bike[start]});
    
    while (!pq.empty()) {
        State current = pq.top();
        pq.pop();
        
        ll current_dist = current.dist;
        int u = current.node;
        bool bike = current.has_bike;
        
        if (current_dist > dist[u + (bike ? n : 0)]) continue;
        
        for (auto &edge : adj[u]) {
            int v = edge.first;
            int w = edge.second;
            
            // 步行状态
            if (!bike) {
                ll new_dist = current_dist + w;
                if (new_dist < dist[v]) {
                    dist[v] = new_dist;
                    pq.push({new_dist, v, has_bike[v]});
                }
            }
            
            // 骑车状态(包括新获得自行车的情况)
            bool new_bike = bike || has_bike[u];
            ll bike_dist = current_dist + (new_bike ? w / 2 : w);
            int bike_state = v + (new_bike ? n : 0);
            
            if (bike_dist < dist[bike_state]) {
                dist[bike_state] = bike_dist;
                pq.push({bike_dist, v, new_bike});
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n, m;
    cin >> n >> m;
    
    for (int i = 0; i < m; ++i) {
        int u, v, w;
        cin >> u >> v >> w;
        adj[u].emplace_back(v, w);
        adj[v].emplace_back(u, w);
    }
    
    int k;
    cin >> k;
    
    for (int i = 0; i < k; ++i) {
        int node;
        cin >> node;
        has_bike[node] = true;
    }
    
    vector<ll> dist(2 * n + 2, INF);
    dijkstra(1, n, dist);
    
    ll result = min(dist[n], dist[2 * n]);
    
    if (result == INF) {
        cout << -1 << endl;
    } else {
        cout << result << endl;
    }
    
    return 0;
}

五、总结

本文通过Dijkstra算法结合状态压缩,高效解决了涉及两种模式(步行/骑车)的最短路径问题。核心在于:

1. 利用节点编号扩展区分状态,降低时间复杂度至O(mlogm)。

2. 动态边权处理与剪枝优化,避免无效状态扩展。

3. 代码结构清晰,注释完整,适合作为算法模板参考。

该解法对图论与状态设计类问题具有通用性,可拓展至其他多模式最短路径场景。


原创内容 转载请注明出处

分享给朋友:

相关文章

力扣198.打家劫舍|动态规划解法中的特殊边界处理

力扣198.打家劫舍|动态规划解法中的特殊边界处理

题意解析:在排列成直线的房屋群中,每个房屋藏有价值不同的财物。小偷不能连续抢劫相邻的两间房屋,否则会触发警报。我们需要设计一套抢劫策略,使得在不触发警报的前提下,能够获取的最大财物总和。这个问题本质上...

IOI 1994 洛谷1216:如何用动态规划高效解决数字三角形问题?附完整代码解析

IOI 1994 洛谷1216:如何用动态规划高效解决数字三角形问题?附完整代码解析

题目重解给定一个由数字组成的三角形结构,从顶部出发,每次可以移动到下方相邻的数字,最终到达底部。我们的目标是找到一条路径,使得路径上经过的数字总和最大。这个问题在实际中有许多应用场景,如最优路径规划、...

力扣94:递归之美 轻松掌握二叉树中序遍历

力扣94:递归之美 轻松掌握二叉树中序遍历

题目解读二叉树的中序遍历是一种基础且重要的树遍历方式,其遍历顺序为:先递归地中序遍历左子树,然后访问根节点,最后递归地中序遍历右子树。对于二叉搜索树,中序遍历的结果恰好是节点值的升序排列。给定一个二叉...

力扣144:递归之美 轻松掌握二叉树前序遍历

力扣144:递归之美 轻松掌握二叉树前序遍历

题目解读二叉树的前序遍历是一种基础但重要的树遍历方式,其遍历顺序为:先访问根节点,然后递归地前序遍历左子树,最后递归地前序遍历右子树。给定一个二叉树的根节点,我们需要按照这个顺序访问所有节点,并将它们...

洛谷4554题解:BFS算法优化最短路径求解(附代码详解)

洛谷4554题解:BFS算法优化最短路径求解(附代码详解)

一、题目解读洛谷4554题要求在一个n×m的网格中,计算从点(x1,y1)到点(x2,y2)的最短路径距离。路径上的每个格子包含字符,若相邻格子字符不同,则需要额外步数。题目核心在于处理字符差异对路径...

力扣第44题:寻找两个正序数组的中位数 - 合并排序解法详解

力扣第44题:寻找两个正序数组的中位数 - 合并排序解法详解

内容简介本文详细解析了力扣第44题"寻找两个正序数组的中位数"的合并排序解法。通过双指针技术合并两个有序数组,然后直接计算合并后数组的中位数。虽然时间复杂度为O(m+n),但这种方...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。