当前位置:首页 > 提高组 > 洛谷P1073题解:最短路问题的SPFA算法优化与双向边处理

洛谷P1073题解:最短路问题的SPFA算法优化与双向边处理

6个月前 (09-07)

洛谷P1073题解:最短路问题的SPFA算法优化与双向边处理 SPFA算法 最短路径 动态规划 NOIP 提高组 图论 队列 有向图 第1张

一、题目解读

洛谷P1073题要求在一个有向图中,通过处理双向边和单向边,计算从起点到终点的最大利润路径。题目中包含两种边:单向边(仅允许单向通行)和双向边(可双向通行),需要利用动态规划思想结合图论算法求解最优解。

二、解题思路

采用SPFA(Shortest Path Faster Algorithm)算法,通过两次最短路径计算实现解题。首先,构建原G和反向图rG,分别处理正向和反向的最短路径。关键在于双向边的特殊处理:若边权为2,则在正反图中均添加该边,确保双向通行性。通过两次SPFA分别计算最小代价和最大代价路径,最终取两者之差的最大值作为答案。

三、解题步骤

1. 读入节点数n、边数m及节点价格数组price。

2. 构建图结构:根据边类型(单向/双向)向G和rG添加边。

3. 运行正向SPFA(min_price数组),计算从起点1到各点的最小价格路径。

4. 运行反向SPFA(max_price数组),计算从终点n到各点的最大价格路径。

5. 遍历各节点,取max_price - min_price的最大值作为最终答案。

6. 输出结果。

四、代码与注释

#include <iostream>  

#include <vector>  

#include <queue>  

#include <algorithm>  

using namespace std;  


const int MAXN = 100010;  

const int INF = 0x3f3f3f3f;  


struct Edge {  

    int to, cost; // 边终点与权值  

};  


vector<Edge> G[MAXN], rG[MAXN]; // 正向图与反向图  

int min_price[MAXN], max_price[MAXN]; // 最小/最大路径价格  

int price[MAXN]; // 节点价格  

bool vis[MAXN]; // 节点访问标记  


// SPFA函数(is_min控制最小/最大路径计算)  

void spfa(int s, vector<Edge> graph[], int dist[], bool is_min) {  

    fill(dist, dist + MAXN, is_min? INF : -INF); // 初始化距离值  

    fill(vis, vis + MAXN, false);  

    queue<int> q;  

    dist[s] = price[s]; // 起点价格作为初始值  

    q.push(s);  

    vis[s] = true;  


    while (!q.empty()) {  

        int u = q.front(); q.pop();  

        vis[u] = false;  

        for (Edge &e : graph[u]) { // 遍历邻居节点  

            int v = e.to;  

            int new_val = is_min? min(dist[u], price[v])  : max(dist[u], price[v]);  // 根据is_min选择min或max  

            if ((is_min && new_val < dist[v]) || (!is_min && new_val > dist[v])) {  

                dist[v] = new_val; // 更新路径值  

                if (!vis[v]) {  

                    q.push(v);  

                    vis[v] = true;  

                }  

            }  

        }  

    }  

}  


int main() {  

    ios::sync_with_stdio(false);  

    cin.tie(nullptr);  


    int n, m;  

    cin >> n >> m;  

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

        cin >> price[i]; // 读入节点价格  

    }  


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

        int x, y, z;  

        cin >> x >> y >> z;  

        G[x].push_back({y, z}); // 添加正向边  

        rG[y].push_back({x, z}); // 添加反向边  

        if (z == 2) { // 双向边特殊处理  

            G[y].push_back({x, z});  

            rG[x].push_back({y, z});  

        }  

    }  


    spfa(1, G, min_price, true); // 正向SPFA计算最小价格  

    spfa(n, rG, max_price, false); // 反向SPFA计算最大价格  


    int ans = 0;  

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

        if (min_price[i]!= INF && max_price[i]!= -INF) {  

            ans = max(ans, max_price[i] - min_price[i]); // 更新最大利润  

        }  

    }  


    cout << ans << endl;  

    return 0;  

}  

五、总结

本文通过SPFA算法结合双向边处理,高效解决了洛谷P1073的最短路问题。关键在于:

1. 构建正反向图以支持双向边通行;

2. 利用min/max分支实现灵活路径计算;

3. 通过两次SPFA遍历获取最优路径差值。

该解法时间复杂度O(mn),适用于稀疏图场景,为动态规划与图论结合的典型实例。


原创内容 转载请注明出处

分享给朋友:

相关文章

力扣70题:告别暴力递归!从零实现记忆化搜索解法

力扣70题:告别暴力递归!从零实现记忆化搜索解法

题意解析:想象你站在楼梯底部,面前有n级台阶。每次你可以选择跨1级或2级台阶,最终到达顶端的路径有多少种不同的走法?这个问题本质上是在探索分叉决策的叠加效果——当我们把每个台阶处的选择看作二叉树的分支...

从零到一掌握背包问题:洛谷P1164题解精讲,附带优化

从零到一掌握背包问题:洛谷P1164题解精讲,附带优化

题目重解:小A带着m元钱来到餐馆,菜单上有n道菜,每道菜都有确定的价格。现在需要计算出刚好花完m元的点菜方案总数。这个问题看似简单,但当菜品数量增多时,暴力枚举就会变得不可行,需要更高效的算法来解决。...

【动态规划入门】力扣509题:斐波那契数列的经典解法与优化思路

【动态规划入门】力扣509题:斐波那契数列的经典解法与优化思路

题目解读‌斐波那契数列是一个经典的数学问题,在计算机科学中常被用作算法教学的入门案例。这个神奇的数列从0和1开始,后续每个数字都是前两个数字之和。题目要求我们计算第n个斐波那契数,看似简单的问题背后却...

LeetCode 120题三角形最小路径和最优解法:动态规划详解与代码实现

LeetCode 120题三角形最小路径和最优解法:动态规划详解与代码实现

一、题目解读LeetCode 120题“三角形最小路径和”要求给定一个由数字组成的三角形,从顶部开始向下移动,每次可向左或向右移动一格,计算从顶至底的最小路径和。三角形以二维向量形式给出,每层元素数量...

洛谷P4999题解析:动态规划求解数字拆分与求和问题(附代码)

洛谷P4999题解析:动态规划求解数字拆分与求和问题(附代码)

一、题目解读洛谷P4999题要求处理给定区间 [L, R] 内数字的拆分与求和问题。每个数字需拆分为其各位数字之和,并计算区间内所有数字之和的累加结果。题目需考虑大数情况,并采用取模运算(MOD=1e...

力扣931题最小下降路径和解析 动态规划解法 LeetCode解题技巧

力扣931题最小下降路径和解析 动态规划解法 LeetCode解题技巧

一、题目解读力扣931题「Minimum Falling Path Sum」(最小下降路径和)要求在一个n x n的整数矩阵中,计算从顶部到底部的最小路径和。路径只能从每个位置向下或对角线移动(即向下...

发表评论

访客

看不清,换一张

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