代码随想录算法训练营第六十一天 | SPFA 算法、Bellman_ford之判断负权回路、Bellman_ford之单源有限最短路

一、SPFA 算法(Bellman_ford 队列优化算法)

题目连接:94. 城市间货物运输 I (kamacoder.com)
文章讲解:代码随想录 (programmercarl.com)——SPFA 算法

与 Bellman_ford 算法区别:只对上一次松弛时更新过的节点,作为出发节点,对其所连接的边进行松弛即可。

from collections import defaultdict, deque
import sys

class Edge:
    def __init__(self, to, val):
        self.to = to
        self.val = val
        
def spfa(n ,edges, start, end):
    # 创建邻接表
    grid = defaultdict(list)
    for p1, p2, val in edges:
        grid[p1].append(Edge(p2, val))
        
    # 初始化最短距离数组
    minDist = [float('inf')] * (n + 1)
    minDist[start] = 0
    
    # 检查节点是否在队列中
    isInDeque = [False] * (n + 1)
    
    # 创建队列
    que = deque()
    que.append(start)
    isInDeque[start] = True
    
    while que:
        node = que.popleft()            # 移除队头元素
        isInDeque[node] = False         # 取出节点时,将其标记未在队列中
        
        # 遍历当前节点所有边
        for edge in grid[node]:
            to_node = edge.to           # 目标节点
            val_node = edge.val         # 边的权重
            
            # 松弛操作更新最短距离
            if minDist[to_node] > minDist[node] + val_node:
                minDist[to_node] = minDist[node] + val_node
                
                # 如果没有访问过,加入队列
                if not isInDeque[to_node]:
                    que.append(to_node)
                    isInDeque[to_node] = True
            
            
    # 检查终点的最短路径距离
    if minDist[end] == float('inf'):
        return "unconnected"
    else:
        return minDist[end]
        

if __name__ == '__main__':
    n, m = map(int, input().split())
    edges = []
    
    # 读取所有边信息
    for _ in range(m):
        p1, p2, val = map(int, input().split())
        edges.append((p1, p2, val))
        
    start = 1
    end = n
    
    result = spfa(n, edges, start, end)
    
    print(result)

二、Bellman_ford之判断负权回路

题目连接:95. 城市间货物运输 II (kamacoder.com)
文章讲解:代码随想录 (programmercarl.com)——Bellman_ford之判断负权回路

题意:判断是否存在 负权回路。

思路:由于具有负权回路,所以每多松弛一次,最小路径就会发生变化,因此可以考虑在Bellman_ford 算法 松弛 n - 1 次的基础上,再多松弛一次,看是否会有变化,如果有变化,则说明存在负权回路。

"""
代码和 Bellman_ford 算法精讲的基本类似,只在变化部分写出注释
"""
def bellman(n, m, edges, start, end):
    minDist = [float('inf')] * (n + 1)
    minDist[start] = 0
    
    # 标记是否存在负权回路
    flag = False
    
    for i in range(1, n + 1):
        for from_node, to_node, val in edges:
            # 松弛前 n - 1 次
            if i < n:
                if minDist[from_node] != float('inf') and minDist[to_node] > minDist[from_node] + val:
                    minDist[to_node] = minDist[from_node] + val
            # 松弛第 n 次
            else:
                # 如果值还在减小,说明存在负权回路
                if minDist[from_node] != float('inf') and minDist[to_node] > minDist[from_node] + val:
                    flag = True
        
    
    # 如过 flag 为 True,则存在负权回路
    if flag:
        return "circle"
    elif minDist[end] == float('inf'):
        return "unconnected"
    else:
        return minDist[end]
        

if __name__ == "__main__":
    n, m = map(int, input().split())
    edges = []
    
    for _ in range(m):
        s, t, v = map(int, input().split())
        edges.append((s, t, v))
        
    start = 1
    end = n
    
    result = bellman(n, m, edges, start, end)
    
    print(result)

三、Bellman_ford之单源有限最短路

题目连接:96. 城市间货物运输 III (kamacoder.com)
文章讲解:代码随想录 (programmercarl.com)——Bellman_ford之单源有限最短路

思路:最多经过 k 个城市的条件下,求最短路径。注意,是最多经过 k 个城市,也可能经过比 k 少的城市。此时,我们最多经过 k + 1 条边,即松弛 k + 1 次。

Note:在每次计算 minDist 时候,要基于 对所有边上一次松弛的 minDist 数值才行,所以我们要记录上一次松弛的minDist。

def bellman(n, m, edges, start, end, k):
    minDist = [float('inf')] * (n + 1)
    minDist[start] = 0
    
    # 创建一个副本数组,用来保存上一次松弛的结果
    minDist_copy = [0] * (n + 1)
    
    # 从 1 到 k + 1 遍历
    for i in range(1, k + 2):
        # 复制当前 minDist
        minDist_copy = minDist.copy()
        
        for from_node, to_node, val in edges:
            # 判断是否可以更新,使用 copy 的数组进行判断
            if minDist_copy[from_node] != float('inf') and minDist[to_node] > minDist_copy[from_node] + val:
                minDist[to_node] = minDist_copy[from_node] + val
                
    if minDist[end] == float('inf'):
        return  "unreachable"
    else:    
        return minDist[end]
        
if __name__ == '__main__':
    n, m = map(int, input().split())
    edges = []
    
    for _ in range(m):
        p1, p2, val = map(int, input().split())
        edges.append((p1, p2, val))
        
    start, end, k = map(int, input().split())
    
    result = bellman(n, m, edges, start, end, k)
    
    print(result)
好的,下面是使用C语言实现Dijkstra算法SPFA算法Bellman-Ford算法代码示例: 1. Dijkstra算法: ```c #include <stdio.h> #include <stdbool.h> #define INF 99999 #define V 6 int minDistance(int dist[], bool visited[]) { int min = INF, min_index; for (int v = 0; v < V; v++) { if (visited[v] == false && dist[v] <= min) { min = dist[v]; min_index = v; } } return min_index; } void printSolution(int dist[]) { printf("Vertex\tDistance from Source\n"); for (int i = 0; i < V; i++) { printf("%d\t%d\n", i, dist[i]); } } void dijkstra(int graph[V][V], int source) { int dist[V]; bool visited[V]; for (int i = 0; i < V; i++) { dist[i] = INF; visited[i] = false; } dist[source] = 0; for (int count = 0; count < V - 1; count++) { int u = minDistance(dist, visited); visited[u] = true; for (int v = 0; v < V; v++) { if (!visited[v] && graph[u][v] && dist[u] != INF && dist[u] + graph[u][v] < dist[v]) { dist[v] = dist[u] + graph[u][v]; } } } printSolution(dist); } int main() { int graph[V][V] = { {0, 4, 0, 0, 0, 0}, {4, 0, 8, 0, 0, 0}, {0, 8, 0, 7, 0, 4}, {0, 0, 7, 0, 9, 14}, {0, 0, 0, 9, 0, 10}, {0, 0, 4, 14, 10, 0} }; dijkstra(graph, 0); return 0; } ``` 2. SPFA算法: ```c #include <stdio.h> #include <stdbool.h> #include <limits.h> #define V 5 // 图中顶点的数量 typedef struct { int u; int v; int weight; } Edge; void printSolution(int dist[]) { printf("Vertex\tDistance from Source\n"); for (int i = 0; i < V; i++) { printf("%d\t%d\n", i, dist[i]); } } void spfa(int graph[V][V], int source) { int dist[V]; bool inQueue[V]; for (int i = 0; i < V; i++) { dist[i] = INT_MAX; inQueue[i] = false; } dist[source] = 0; // 创建一个队列,用于存储顶点索引 int queue[V]; int front = -1; int rear = -1; // 将起始顶点加入队列 queue[++rear] = source; inQueue[source] = true; while (front != rear) { int u = queue[++front]; inQueue[u] = false; for (int v = 0; v < V; v++) { if (graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v]) { dist[v] = dist[u] + graph[u][v]; if (!inQueue[v]) { queue[++rear] = v; inQueue[v] = true; } } } } printSolution(dist); } int main() { int graph[V][V] = { {0, 4, 0, 0, 0}, {4, 0, 8, 0, 0}, {0, 8, 0, 7, 0}, {0, 0, 7, 0, 9}, {0, 0, 0, 9, 0} }; spfa(graph, 0); return 0; } ``` 3. Bellman-Ford算法: ```c #include <stdio.h> #include <stdbool.h> #include <limits.h> #define V 5 // 图中顶点的数量 #define E 8 // 图中边的数量 typedef struct { int u; int v; int weight; } Edge; void printSolution(int dist[]) { printf("Vertex\tDistance from Source\n"); for (int i = 0; i < V; i++) { printf("%d\t%d\n", i, dist[i]); } } void bellmanFord(Edge edges[], int source) { int dist[V]; for (int i = 0; i < V; i++) { dist[i] = INT_MAX; } dist[source] = 0; for (int i = 1; i < V; i++) { for (int j = 0; j < E; j++) { int u = edges[j].u; int v = edges[j].v; int weight = edges[j].weight; if (dist[u] != INT_MAX && dist[u] + weight < dist[v]) { dist[v] = dist[u] + weight; } } } // 检查是否存在负权回路 for (int i = 0; i < E; i++) { int u = edges[i].u; int v = edges[i].v; int weight = edges[i].weight; if (dist[u] != INT_MAX && dist[u] + weight < dist[v]) { printf("Graph contains negative weight cycle\n"); return; } } printSolution(dist); } int main() { Edge edges[E] = { {0, 1, -1}, {0, 2, 4}, {1, 2, 3}, {1, 3, 2}, {1, 4, 2}, {3, 2, 5}, {3, 1, 1}, {4, 3, -3} }; bellmanFord(edges, 0); return 0; } ``` 以上是使用C语言实现Dijkstra算法SPFA算法Bellman-Ford算法代码示例。希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值