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

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

22小时前

洛谷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)),确保在实际应用中的可行性。

原创内容 转载请注明出处

分享给朋友:

相关文章

【深度优先搜索实战】力扣547题:省份数量问题的图论解法

【深度优先搜索实战】力扣547题:省份数量问题的图论解法

题目解读‌我们面对的是一个典型的图论问题:给定一个城市的连接矩阵,需要计算其中相互连通的城市群(省份)数量。这个问题可以抽象为无向图中的连通分量计算,每个城市代表图中的一个节点,城市之间的连接关系代表...

力扣第654题:最大二叉树解题教程 用数组构造最大二叉树

力扣第654题:最大二叉树解题教程 用数组构造最大二叉树

题目解读给定一个不含重复元素的整数数组,我们需要构建一棵最大二叉树。构建规则是:数组中的最大值作为根节点,其左侧子数组构建左子树,右侧子数组构建右子树,然后递归地应用这个规则。这种构建方式体现了分治思...

洛谷P4551题解题报告:图论与Trie树优化异或路径问题的实战解析

洛谷P4551题解题报告:图论与Trie树优化异或路径问题的实战解析

一、题目解读洛谷P4551题要求在一个无向图中,寻找任意两点路径权值异或后的最大值。题目输入为图的边信息(点数n和n-1条边),每条边包含起点、终点及权值。需输出所有路径中权值异或的最大值。问题核心在...

洛谷1111题解题全解析:基于Kruskal算法与并查集的最小生成树实现

洛谷1111题解题全解析:基于Kruskal算法与并查集的最小生成树实现

一、题目解读洛谷1111题是一道经典的图论问题,要求构建一个无向图的最小生成树,并输出其最大边权值。题目核心在于通过给定的边集合,找到连接所有节点的最小权值子集,同时保证无环。这通常涉及最小生成树算法...

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

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

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

标题:洛谷B3617题解析:八进制转十六进制算法实现与优化(附AC100代码)

标题:洛谷B3617题解析:八进制转十六进制算法实现与优化(附AC100代码)

一、题目解读洛谷B3617题要求将输入的八进制字符串转换为十六进制表示。题目需处理大数场景,且对输入合法性有明确限制(长度不超过1000,仅包含0-7字符)。由于八进制与十六进制无法直接转换,需借助十...

发表评论

访客

看不清,换一张

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