C - Heavy Transportation——spfa()算法

本文介绍了一种基于SPFA算法的最短路径问题变形求解方法,重点在于如何找到从起点到终点所有可达路径中权值最小的最大值。通过对比正确与错误的实现代码,强调了数组初始化的重要性。

Think:
1知识点:spfa()算法+最短路径变形
2题意:求n = 1的点到 n = n 的所有可达路径中(当前路径权值最小的)最大值
3反思:注意不要忘记e数组初始化

以下为Wrong Answer代码——题意理解错误

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>

using namespace std;

const int N = 1e3 + 4;
const int inf = 0x3f3f3f3f;

int n, e[N][N], dis[N], vis[N];

void spfa();

int main(){
    int T, k, m, i, u, v, w;
    scanf("%d", &T);
    for(k = 1; k <= T; k++){
        scanf("%d %d", &n, &m);
        memset(e, 0, sizeof(e));
        for(i = 1; i <= m; i++){
            scanf("%d %d %d", &u, &v, &w);
            e[u][v] = e[v][u] = w;
        }
        spfa();
        printf("Scenario #%d:\n", k);
        printf("%d\n", dis[n]);
        printf("\n");
    }
    return 0;
}
void spfa(){
    queue <int> q;
    memset(dis, inf, sizeof(dis));
    memset(vis, 0, sizeof(vis));

    q.push(1), dis[1] = 0, vis[1] = 1;
    while(!q.empty()){
        int t1 = q.front();
        q.pop();
        vis[t1] = 0;
        for(int v = 1; v <= n; v++){
            if(max(dis[t1], e[t1][v]) < dis[v]){
                dis[v] = max(dis[t1], e[t1][v]);
                if(!vis[v]){
                    q.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
}

以下为Wrong Answer代码——e数组未初始化

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int inf = 0x3f3f3f3f;
const int N = 1014;

int n, m, e[N][N], vis[N], dis[N];

void spfa(int x);

int main(){
    int k = 1, T, i, u, v, w;
    scanf("%d", &T);
    while(T--){
        scanf("%d %d", &n, &m);
        for(i = 0; i < m; i++){
            scanf("%d %d %d", &u, &v, &w);
            e[u][v] = e[v][u] = w;
        }
        spfa(1);
        printf("Scenario #%d:\n", k++);
        printf("%d\n", dis[n]);
        printf("\n");
    }
    return 0;
}
void spfa(int x){
    queue <int> q;
    while(!q.empty()){
        q.pop();
    }
    memset(vis, 0, sizeof(vis));
    memset(dis, 0, sizeof(dis));
    vis[x] = 1, dis[x] = inf;
    q.push(x);
    while(!q.empty()){
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for(int i = 1; i <= n; i++){
            if(min(dis[u], e[u][i]) > dis[i]){
                dis[i] = min(dis[u], e[u][i]);
                if(!vis[i]){
                    vis[i] = 1;
                    q.push(i);
                }
            }
        }
    }
}

以下为Accepted代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int inf = 0x3f3f3f3f;
const int N = 1014;

int n, m, e[N][N], vis[N], dis[N];

void spfa(int x);

int main(){
    int k = 1, T, i, u, v, w;
    scanf("%d", &T);
    while(T--){
        scanf("%d %d", &n, &m);
        memset(e, 0, sizeof(e));/*注意不要忘记初始化*/
        for(i = 0; i < m; i++){
            scanf("%d %d %d", &u, &v, &w);
            e[u][v] = e[v][u] = w;
        }
        spfa(1);
        printf("Scenario #%d:\n", k++);
        printf("%d\n", dis[n]);
        printf("\n");
    }
    return 0;
}
void spfa(int x){
    queue <int> q;
    while(!q.empty()){
        q.pop();
    }
    memset(vis, 0, sizeof(vis));
    memset(dis, 0, sizeof(dis));
    vis[x] = 1, dis[x] = inf;
    q.push(x);
    while(!q.empty()){
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for(int i = 1; i <= n; i++){
            if(min(dis[u], e[u][i]) > dis[i]){
                dis[i] = min(dis[u], e[u][i]);
                if(!vis[i]){
                    vis[i] = 1;
                    q.push(i);
                }
            }
        }
    }
}
<think>嗯,用户现在想了解Bellman-Ford和SPFA算法的区别。之前讨论过连通分量算法,现在突然转向短路径问题,可能是学习进度推进到图论的新章节了。用户特意提到站内引用,说明ta手头有参考资料但需要更清晰的对比分析。 从引用内容看,用户提供的资料其实有些错位——引用[1][2][3]主要讲Bellman-Ford和SPFA,但引用[4]突然跳到Floyd算法心得,而标题里提到的"短路径Bellman-Ford算法"等引用内容实际并未出现。不过核心信息足够用:引用[1]展示Bellman-Ford的伪代码,引用[2]说明SPFA是队列优化的Bellman-Ford,引用[3]指出其局限性和改进方向。 用户应该刚学完Dijkstra算法(引用[4]提到"学完短路径需要用的几种著名算法"),现在接触带负权边的情况。ta的提问很精准——没有问基础原理而是直接要求对比差异,说明至少看过伪代码。但可能被这两个相似算法的关系搞糊涂了,需要厘清SPFA到底"优化"在哪里。 需要强调三个关键差异点:1)迭代方式(全边扫描 vs 动态更新)2)时间复杂度差异 3)负环检测的特殊处理。表格对比会很直观,但要注意用户提供的引用里提到SPFA时间复杂度是$O(kE)$(k平均为2),这点必须标注清楚,因为教材常写$O(VE)$可能让用户困惑。 突然想到用户可能混淆了"优化"的含义——SPFA不是改变Bellman-Ford的理论复杂度,而是通过减少无效松弛来提升实际效率。这个点要在回答中用实验数据案例说明(比如网格图退化情况)。另外必须警告SPFA坏情况,有些ACM选手会忽略这点导致比赛TLE。 后补充负环检测的细节差异,用户给的引用[3]提到"负权环无法处理"但没具体说,这里要明确Bellman-Ford通过额外迭代检测,SPFA则用入队次数。实用建议部分很重要——用户肯定是想写代码,要给出算法选择决策树:有负权吗?→图稀疏吗?→需要检测负环吗?... </think> Bellman-Ford算法SPFA算法均用于解决**带负权边的单源短路径问题**,二者的核心区别在于**迭代策略和效率优化**。以下是详细对比分析: --- ### **1. Bellman-Ford算法** #### **算法思想** 通过$V-1$轮($V$为顶点数)全边松弛操作,逐步逼近短路径。每轮遍历**所有边**,更新源点到各顶点的距离。 **伪代码**: ```python for i in range(V-1): # 进行V-1轮迭代 for each edge u->v: # 遍历所有边 if d[u] + w(u,v) < d[v]: d[v] = d[u] + w(u,v) ``` #### **关键特性** - **时间复杂度**:$O(V \cdot E)$ - **负环检测**:完成$V-1$轮后,**再执行一轮全边松弛**。若仍有距离更新,说明存在负权环[^3]。 - **优点**:实现简单,能处理负权边并检测负环。 - **缺点**:大量冗余计算(即使距离未更新仍遍历所有边),效率低[^1][^3]。 --- ### **2. SPFA算法(队列优化的Bellman-Ford)** #### **算法思想** 基于队列动态管理待松弛顶点,仅当顶点距离更新时才将其邻边加入松弛队列。 **伪代码**: ```python queue.push(source) # 源点入队 while queue not empty: u = queue.pop() for each neighbor v of u: if d[u] + w(u,v) < d[v]: d[v] = d[u] + w(u,v) if v not in queue: # 若v不在队列中则入队 queue.push(v) ``` #### **关键特性** - **时间复杂度**:**平均$O(kE)$**($k$为常数,通常$k \approx 2$),坏$O(V \cdot E)$[^2][^4]。 - **负环检测**:记录顶点**入队次数**。若某顶点入队次数$\geq V$,说明存在负环[^3]。 - **优点**:**大幅减少冗余计算**,平均效率显著优于Bellman-Ford。 - **缺点**:**坏情况效率与Bellman-Ford相同**(如网格图),且队列操作增加常数开销[^2]。 --- ### **3. 核心区别对比** | **特性** | **Bellman-Ford** | **SPFA** | |------------------|-----------------------------|------------------------------| | **迭代方式** | 固定$V-1$轮全边扫描 | 队列动态管理待松弛顶点 | | **时间复杂度** | $O(V \cdot E)$ | 平均$O(kE)$,坏$O(V \cdot E)$ | | **空间复杂度** | $O(V)$ | $O(V + E)$(需存储队列和邻接表) | | **负环检测** | 额外一轮全边松弛 | 统计顶点入队次数 | | **实际效率** | 低(大量无效松弛) | 高(仅松弛可能更新的边) | | **适用场景** | 稠密图或需简单实现时 | 稀疏图且需高效处理负权边时 | --- ### **4. 为什么SPFA更高效?** SPFA通过队列避免无效松弛: - 只有**距离被更新的顶点**才会触发其邻边的松弛操作。 - 例如:若顶点$u$的距离未变化,则无需检查$u \to v$的边[^2]。 > ⚠️ **注意**:SPFA在**特定图结构(如网格图)中会退化**为$O(V \cdot E)$,此时Bellman-Ford的稳定性更优。 --- ### **5. 选择建议** - **优先SPFA**:稀疏图且无负环嫌疑时(如网络路由优化)。 - **使用Bellman-Ford**:需严格检测负环,或图结构易导致SPFA退化时。 - **替代方案**:无负权边时,**Dijkstra算法**($O(E \log V)$)更高效。 --- ### **
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值