当前位置:首页 > 洛谷 > 洛谷P3393题解:基于多源BFS与Dijkstra算法求解图论最小花费路径问题

洛谷P3393题解:基于多源BFS与Dijkstra算法求解图论最小花费路径问题

6天前

洛谷P3393题解:基于多源BFS与Dijkstra算法求解图论最小花费路径问题 洛谷题解 图论算法 多源BFS Dijkstra算法 优先队列 C++ 第1张

一、题目解读

洛谷P3393题要求在一个包含N个城市和M条双向道路的中,求解从起点1到终点N的最小花费路径。图中存在僵尸城市(僵尸无法通过)和危险城市(由僵尸城市扩散S步后标记),需要避开所有危险城市,同时每个城市有住宿费用。题目保证有解,需利用图论算法优化路径计算。

二、解题思路

1. 多源BFS标记危险城市:由于僵尸城市会向四周扩散S步形成危险区域,采用多源BFS遍历所有僵尸城市,标记其S步内可达的城市为危险(isDanger数组)。

2. Dijkstra算法求最短路:在避开危险城市的前提下,利用Dijkstra从起点开始计算到终点N的最小总花费(住宿费用累加)。

3. 优化点:通过优先队列维护当前最短路径,跳过僵尸城市以减少无效搜索,确保算法效率。

三、解题步骤

1. 数据初始化:读取城市数量N、道路数M、僵尸城市数K和扩散步数S,构建邻接表graph存储图结构,初始化危险、僵尸标记及费用数组。

2. 标记危险城市:调用markDangerCities函数,对所有僵尸城市进行BFS,使用队列记录当前城市和步数,当步数超过S时停止扩散。

3. 执行Dijkstra算法:

    初始化起点1的最小花费为0,其余城市为无穷大。

    用优先队列维护当前待扩展节点(距离,城市编号)。

    循环直至终点N被访问或队列为空,每次取出当前距离最小的节点u,遍历其邻居v:

        若v为僵尸城市或危险城市,跳过。

        计算新路径花费(当前距离+城市v的住宿费用),若更优则更新dist[v]并加入队列。

4. 输出结果:返回终点N的最小花费,若无法到达则为-1(但题目保证有解)。

四、代码与注释

#include <iostream>

#include <vector>

#include <queue>

#include <climits>

using namespace std;


typedef long long ll;

const int MAXN = 1e5 + 5;


vector<int> graph[MAXN];  // 邻接表存储图

bool isDanger[MAXN];      // 标记危险城市

bool isZombie[MAXN];      // 标记僵尸城市

ll cost[MAXN];            // 每个城市的住宿费用

ll dist[MAXN];            // 到每个城市的最小花费


// 多源BFS计算危险城市

void markDangerCities(int N, int S) {

    queue<pair<int, int>> q;

    vector<int> dangerDist(N + 1, -1);  // 记录危险扩散距离

    

    // 初始化僵尸城市

    for (int i = 1; i <= N; ++i) {

        if (isZombie[i]) {  // 若为僵尸城市

            q.push({i, 0});  // 入队,步数初始化为0

            dangerDist[i] = 0;

        }

    }

    

    // BFS扩展

    while (!q.empty()) {

        auto [u, d] = q.front();  // 当前城市和步数

        q.pop();

        

        if (d >= S) continue;  // 超过扩散步数,停止扩散

        

        for (int v : graph[u]) {  // 遍历邻居

            if (dangerDist[v] == -1) {  // 未标记过

                dangerDist[v] = d + 1;  // 更新距离

                q.push({v, d + 1});

                isDanger[v] = true;  // 标记为危险

            }

        }

    }

}


// Dijkstra算法求最短路

ll dijkstra(int N) {

    priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> pq;  // 小根堆优先级队列

    fill(dist, dist + N + 1, LLONG_MAX);  // 初始化距离为无穷大

    

    dist[1] = 0;  // 起点距离为0

    pq.push({0, 1});  // 起点入队

    

    while (!pq.empty()) {

        auto [currentDist, u] = pq.top();  // 取出当前距离最小的节点

        pq.pop();

        

        if (u == N) return currentDist;  // 到达终点,返回路径花费

        if (currentDist > dist[u]) continue;  // 跳过已优化过的节点

        

        for (int v : graph[u]) {  // 遍历邻居

            if (isZombie[v]) continue;  // 若为僵尸城市,跳过

            ll newDist = currentDist + cost[v];  // 计算新路径花费

            if (newDist < dist[v]) {  // 更优路径

                dist[v] = newDist;

                pq.push({newDist, v});

            }

        }

    }

    return -1;  // 题目保证有解,实际不会执行

}


int main() {

    ios::sync_with_stdio(false);

    cin.tie(nullptr);

    

    int N, M, K, S;

    cin >> N >> M >> K >> S;

    

    int P, Q;

    cin >> P >> Q;  

   

    // 读取僵尸城市

    for (int i = 0; i < K; ++i) {

        int c;

        cin >> c;

        isZombie[c] = true;

    }

    

    // 读取道路

    for (int i = 0; i < M; ++i) {

        int a, b;

        cin >> a >> b;

        graph[a].push_back(b);

        graph[b].push_back(a);

    }

    

    // 标记危险城市

    markDangerCities(N, S);

    

    // 设置每个城市的住宿费用

    for (int i = 1; i <= N; ++i) {

        if (i == 1 || i == N) {

            cost[i] = 0;  // 起点和终点不需要住宿

        } else if (isZombie[i]) {

            cost[i] = LLONG_MAX;  // 不能进入僵尸城市

        } else if (isDanger[i]) {

            cost[i] = Q;

        } else {

            cost[i] = P;

        }

    }

    

    cout << dijkstra(N) << endl;

    

    return 0;

}

五、总结

本文通过结合多源BFS与Dijkstra算法,高效解决了洛谷P3393题中的图论路径问题。关键在于预处理危险城市范围,避免无效搜索,同时利用优先队列优化路径选择。代码简洁且符合题目要求,为类似图论问题提供了可行的解题思路。需注意题目数据范围和算法时间复杂度分析(O(N+MlogN)),确保在实际应用中的可行性。

原创内容 转载请注明出处

分享给朋友:

相关文章

力扣53题:贪心策略与动态规划的完美联姻 三行代码映射算法精髓

力扣53题:贪心策略与动态规划的完美联姻 三行代码映射算法精髓

题目理解在数字的海洋中寻找最具价值的珍珠链:当我们面对一个可能包含正负数的数组时,寻找连续子数组的和最大值就像在波动的股票曲线中捕捉最佳投资时段。问题的核心在于如何处理可能降低总和的负值元素——是忍痛...

力扣1221:一次扫描解决分割平衡字符串 时间O(n)空间O(1)

力扣1221:一次扫描解决分割平衡字符串 时间O(n)空间O(1)

题目重解给定一个仅包含'L'和'R'的字符串,要求将其分割成尽可能多的子串,且每个子串中'L'和'R'的数量相等。例如输入"R...

征服力扣704题:三步掌握经典二分查找算法

征服力扣704题:三步掌握经典二分查找算法

题目重解我们面对的是算法领域最经典的二分查找问题:在一个已排序的整数数组中,快速定位目标值的位置。就像在一本按字母顺序排列的字典中查找单词,我们不需要逐页翻阅,而是通过不断折半的方式快速缩小搜索范围,...

2017年 NOIP 提高组 逛公园(洛谷P3953)题解:代码解析与优化

2017年 NOIP 提高组 逛公园(洛谷P3953)题解:代码解析与优化

一、题目解读    2017年NOIP提高组“逛公园”题目(洛谷P3953)要求在有向图中计算从起点到终点满足特定条件的路径数量。题目难点在于处理路径长度限制与...

力扣3112题解法:带时间限制的最短路径问题解析(C++代码)

力扣3112题解法:带时间限制的最短路径问题解析(C++代码)

一、题目解读力扣3112题要求解决带时间限制的最短路径问题:给定一个有向图,节点具有消失时间,需计算从起点到各节点的最短路径,且路径总时间不能超过节点的消失时间。题目难点在于需在传统最短路径算法(如D...

【洛谷1184题解析】用C++高效解决地点匹配问题(附代码与解题思路)

【洛谷1184题解析】用C++高效解决地点匹配问题(附代码与解题思路)

一、题目解读洛谷1184题要求处理一组地点列表与行程记录,统计其中匹配的天数。题目难点在于高效处理带有空格的字符串输入,以及快速判断每日行程是否在高手可去地点集合中。需要兼顾输入格式解析与算法效率。二...

发表评论

访客

看不清,换一张

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