最短路算法合集

Llama Factory

Llama Factory

模型微调
LLama-Factory

LLaMA Factory 是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。通过 LLaMA Factory,可以在无需编写任何代码的前提下,在本地完成上百种预训练模型的微调

最短路算法合集:

一、朴素版dijkstra

合适的使用范围:无负权边的稠密图
时间复杂度: O(n2)O(n^2)O(n2)

实现方式:按点更新,用当前最近的没有更新到的点更新其他没更新到的点。第一层枚举nnn次,第二层1判断最近的点,第二层2更新其他还没更新到的点。
代码实现:

int dijkstra() {
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0; 
    for (int i = 1; i < n; i++) {
        int t = -1;
        for (int j = 1; j <= n; j++)
            if (!st[j] && (t == -1 || dist[j] < dist[t])) t = j;
        st[t] = true;
        for (int j = 1; j <= n; j++)
            dist[j] = min(dist[j], dist[t] + g[t][j]);
    }
    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
} 

如果有负权边,则可能使得最短路比原来的长。举例:
1−2−4,1−3−5,2−3−(−3)1-2-4,1-3-5,2-3-(-3)12413523(3)。可以发现 111333 的最短路为 111 ,但由于dijkstra的贪心思想,会在还没走 2−32-323 这条边之前就将1-2的路径更新了。

二、堆优化版dijkstra

合适的使用范围:无负权边的稀疏图
时间复杂度: O(mlogm)O(mlogm)O(mlogm)

实现方式:与朴素版dijkstra相似,但是,是用最短边去更新没有更新到的点。第一层枚举最短边,第二层更新没有更新到的点。
代码实现:

struct node {
    int x, y;
    bool operator < (const node &a) const {
        return y > a.y;
    }
};
int n, m;
int h[N], e[N], w[N], ne[N], idx;
int dist[N];
bool st[N];
void add(int a, int b, int c) {
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int dijkstra() {
    memset(dist, 0x3f, sizeof(dist));
    priority_queue<node> q;
    dist[1] = 0;
    q.push({1, 0});
    while (q.size()) {
        node t = q.top();
        q.pop();
        if (st[t.x]) continue;
        for (int i = h[t.x]; i != -1; i = ne[i]) {
            if (dist[e[i]] > dist[t.x] + w[i]) {
                dist[e[i]] = dist[t.x] + w[i];
                q.push({e[i], dist[e[i]]});
            }
        }
        st[t.x] = true;
    }
    if (dist[n] == 0x3f3f3f3f) return -1;
    else return dist[n];
}

同样如果有负权边,则无法判断是否存在最短路。

三、Bellman-ford

合适的使用范围:有负权边且有最短路边数限制的图(可以判断负环)
时间复杂度:O(nm)O(nm)O(nm)

实现方式:第一次枚举nnn次,第二次枚举mmm条边,可以证明n次迭代后,如果有最短路必能求出,枚举k次即k条边的最短路。
注意:当用该算法求k条边的最短路时要有备份backup[n],原因在于在一次 nnn 的枚举中不能用已经更新过的点去更新其他点。最后判断有无最短路时要用已得距离与一个较大的数进行比较。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 510, M = 10010;

struct Edge {
    int a, b, c;
} edge[M];

int n, m, k;
int dist[N], backup[N];

int Bellman_ford() {
    memset(dist, 0x3f, sizeof(dist));
    dist[1] = 0;
    for (int i = 0; i < k; i++) {
        memcpy(backup, dist, sizeof(dist));
        for (int j = 0; j < m; j++) 
            if (dist[edge[j].b] > backup[edge[j].a] + edge[j].c)
                dist[edge[j].b] = backup[edge[j].a] + edge[j].c;
    }
    
    if (dist[n] > 0x3f3f3f3f / 2) return -1;
    else return dist[n];
}

int main() {
    scanf("%d %d %d", &n, &m, &k);
    for (int i = 0; i < m; i++) {
        int a, b, c;
        scanf("%d %d %d", &a, &b, &c);
        edge[i].a = a, edge[i].b = b, edge[i].c = c;
    }
    if (Bellman_ford() == -1) printf("impossible\n");
    else printf("%d\n", dist[n]);
    return 0;
}

四、SPFA

合适的适用范围:有负权边且没最短路边数的限制的图(可以判断负环)
时间复杂度:一般O(m)O(m)O(m),最坏O(nm)O(nm)O(nm)

实现方式:与Bellman_ford算法相似,不同之处在于要用一个队列来存储改变了值的点,节点的值没变的则不枚举。所以,该算法也与堆优化的dijkstra算法十分相似。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

const int N = 1e5 + 10;

int n, m;
int h[N], e[N], w[N], ne[N], idx;
int dist[N];
bool st[N];

void add(int a, int b, int c) {
    e[idx] = b;
    w[idx] = c;
    ne[idx] = h[a];
    h[a] = idx++;
}

int spfa() {
    memset(dist, 0x3f, sizeof(dist));
    dist[1] = 0;
    queue<int> q;
    q.push(1);
    st[1] = true;
    while (q.size()) {
        int t = q.front();
        q.pop();
        st[t] = false;
        for (int i = h[t]; i != -1; i = ne[i]) {
            int j = e[i];
            if (dist[j] > dist[t] + w[i]) {
                dist[j] = dist[t] + w[i];
                if (!st[j]) {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }
    return dist[n];
}

int main() {
    memset(h, -1, sizeof(h));
    scanf("%d %d", &n, &m);
    for (int i = 0; i < m; i++) {
        int a, b, c;
        scanf("%d %d %d", &a, &b, &c);
        add(a, b, c);
    }
    if (spfa() == 0x3f3f3f3f) printf("impossible\n");
    else printf("%d\n", dist[n]);
    return 0;
}

五、Floyd

算法实现:动态规划,枚举中间点,中间点首先枚举,可以有负权边,但不能有负环。
时间复杂度:O(n3)O(n^3)O(n3)
例题

给定一个n个点m条边的有向图,图中可能存在重边和自环,边权可能为负数。

再给定k个询问,每个询问包含两个整数x和y,表示查询从点x到点y的最短距离,如果路径不存在,则输出“impossible”。

数据保证图中不存在负权回路。

#include <cstring>
#include <iostream>
using namespace std;
const int N = 210, INF = 1e9;
int n, m, Q;
int d[N][N];
void floyd() {
    for (int k = 1; k <= n; k ++ )
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
int main(){
    scanf("%d%d%d", &n, &m, &Q);
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            if (i == j) d[i][j] = 0;
            else d[i][j] = INF;
    while (m -- ) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        d[a][b] = min(d[a][b], c);
    }
    floyd();
    while (Q -- ) {
        int a, b;
        scanf("%d%d", &a, &b);
        int t = d[a][b];
        if (t > INF / 2) puts("impossible");
        else printf("%d\n", t);
    }
    return 0;
}

六、SPFA判断负环

实现方式:根据SPFA的实现原理我们可以知道当所有的点都无法更新的时候算法就会结束了,但如果有负环的话则会导致程序无法结束。根据抽屉原理我们可以知道,一个有 nnn 个节点的图,如果没出现环的话,那么从一个点到另一个的最短路的最多经过n−1n - 1n1个点,所以我们可以根据转移数量来判定一个图中有没有负环。

#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int h[N], e[N], ne[N], w[N], idx;
int dist[N];
bool st[N];
int cnt[N];
void add(int a, int b, int c) {
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int spfa() {
    queue<int> q;
    for (int i = 1; i <= n; i++) {
        st[i] = true;
        q.push(i);
    }
    while (q.size()) {
        int t = q.front();
        q.pop();
        st[t] = false;
        for (int i = h[t]; i != -1; i = ne[i]) {
            int j = e[i];
            if (dist[j] > dist[t] + w[i]) {
                dist[j] = dist[t] + w[i];
                cnt[j] = cnt[t] + 1;
                if (cnt[j] >= n) return true;
                if (!st[j]) {
                    st[j] = true;
                    q.push(j);
                }
            }
        }
    }
    return false;
}
int main() {
    memset(h, -1, sizeof(h));
    scanf("%d %d", &n, &m);
    for (int i = 0; i < m; i++) {
        int a, b, c;
        scanf("%d %d %d", &a, &b, &c);
        add(a, b, c);
    }
    if (spfa()) printf("Yes\n");
    else printf("No\n");
    return 0;
}

七、对最短路问题的总结:

1.多源汇最短路:Floyd
2.k条边最短路:Bellman_ford
3.带负权的边、判重边:SPFA
4.稠密图:朴素版dijkstra
5.稀疏图:堆优化dijkstra

您可能感兴趣的与本文相关的镜像

Llama Factory

Llama Factory

模型微调
LLama-Factory

LLaMA Factory 是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。通过 LLaMA Factory,可以在无需编写任何代码的前提下,在本地完成上百种预训练模型的微调

### 关于短路径问题的经典例题 在解决短路径问题时,通常会涉及多种算法的应用场景。下面列举了一些经典的短路径问题及其对应的例题。 #### Dijkstra 算法经典例题 Dijkstra 算法适用于加权有向图中的单源短路径计算,其中边权重均为非负数。以下是一个典型的例子: - **POJ 2387 - Til the Cows Come Home** 这道题目描述了一个牧场地图上的牛棚布局,要求找到从起点到终点的短路径长度。可以使用 Dijkstra 算法来解决问题[^1]。 ```python import heapq def dijkstra(graph, start): distances = {node: float('inf') for node in graph} distances[start] = 0 priority_queue = [(0, start)] while priority_queue: current_distance, current_node = heapq.heappop(priority_queue) if current_distance > distances[current_node]: continue for neighbor, weight in graph[current_node].items(): distance = current_distance + weight if distance < distances[neighbor]: distances[neighbor] = distance heapq.heappush(priority_queue, (distance, neighbor)) return distances ``` --- #### Floyd-Warshall 算法经典例题 Floyd-Warshall 算法用于多源短路径问题,适合处理稠密图或需要全局优解的情况。以下是一道典型题目: - **UVA 247 - Calling Circles** 题目要求判断一组电话号码之间的通话关系是否形成循环依赖。通过构建邻接矩阵并应用 Floyd-Warshall 算法,能够高效地检测任意两点间的可达性和短路径[^4]。 ```python def floyd_warshall(distances): n = len(distances) for k in range(n): for i in range(n): for j in range(n): if distances[i][k] + distances[k][j] < distances[i][j]: distances[i][j] = distances[i][k] + distances[k][j] return distances ``` --- #### Bellman-Ford 算法经典例题 Bellman-Ford 算法支持带有负权重边的图结构,尽管效率较低但仍具有广泛适用性。以下为一道相关题目: - **LeetCode 743 - Network Delay Time** 给定一张带延迟时间的通信网络图,目标是从某个节点发送信号后,确定整个网络完全接收所需的大时间。此问题可以通过 Bellman-Ford 或优化后的 SPFA 实现[^3]。 ```python def bellman_ford(edges, num_nodes, source): distances = [float('inf')] * (num_nodes + 1) distances[source] = 0 for _ in range(num_nodes - 1): for u, v, w in edges: if distances[u] != float('inf') and distances[u] + w < distances[v]: distances[v] = distances[u] + w # Check for negative cycles for u, v, w in edges: if distances[u] != float('inf') and distances[u] + w < distances[v]: raise ValueError("Graph contains a negative cycle") return distances ``` --- #### 第二短路径问题经典例题 除了常规的短路径外,有时还需要考虑次优路径(第二短路径)。这类问题往往更加复杂,因为允许重复经过某些顶点或边。以下提供一例: - **Codeforces Round #XYZ Problem A - Second Shortest Path** 输入一个无向连通图以及起始和终止位置,输出严格大于短路径的第一候选路径长度。此类问题可基于 BFS 和 DFS 的组合策略加以扩展求解[^2]。 ```python from collections import deque def bfs_second_shortest_path(graph, start, end): queue = deque([(start, 0)]) visited = set() first_min = second_min = float('inf') while queue: node, dist = queue.popleft() if node == end: if dist < first_min: second_min = first_min first_min = dist elif first_min < dist < second_min: second_min = dist if dist >= second_min: continue for neighbor in graph[node]: if (node, neighbor) not in visited: visited.add((node, neighbor)) queue.append((neighbor, dist + 1)) return second_min if second_min != float('inf') else -1 ``` --- ### 总结 上述例题涵盖了不同类型的短路径应用场景,包括但不限于单源、多源及特殊条件下的变体问题。每种方法均有其特定优势与局限,在实际开发过程中需根据具体需求灵活选用相应工具和技术。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值