Bellman-Ford算法 C++

Bellman-Ford算法是一种解决最短路径问题的动态规划算法,该问题是求解从源节点到其他节点的最短路径。与Dijkstra算法不同的是,Bellman-Ford算法可以处理带有负权边的图。该算法的时间复杂度为O(V*E),其中V是节点的数量,E是边的数量。

Bellman-Ford算法的原理如下:
1. 初始化所有节点的距离为无穷大,源节点的距离为0。
2. 进行V-1次循环,每次循环遍历所有的边,对于每个边(u,v),如果通过u节点可以获得更短的距离到达v节点,则更新v节点的距离为新的更短距离。
3. 检测是否存在负权回路。再进行一次循环遍历所有的边,如果在这次循环中仍然存在节点的距离被更新,则说明存在负权回路。

使用Bellman-Ford算法可以解决以下问题:
1. 单源最短路径:给定一个源节点,求解从源节点到其他节点的最短路径和距离。
2. 检测负权回路:判断图中是否存在负权回路。

由于Bellman-Ford算法可以处理负权边,但是不能处理负权回路。如果存在负权回路,则算法会进入无限循环。因此,在实际应用中,需要注意检测负权回路的存在,可以通过增加一个计数器来限制循环次数,避免死循环的发生。

优点:
1. 适用范围广:Bellman-Ford算法适用于有向图和带负权边的情况,这使得它在实际应用中非常有用。
2. 可以处理负权边:相比于Dijkstra算法,Bellman-Ford算法可以处理负权边。这使得它在某些场景下更为适用。
3. 可以检测负权环:Bellman-Ford算法可以检测是否存在负权环。如果存在负权环,则说明图中不存在最短路径。

缺点:
1. 时间复杂度高:Bellman-Ford算法的时间复杂度为O(VE),其中V是顶点数,E是边数。这使得它在有大量顶点和边的图中效率较低。

Bellman-Ford算法的代码实现

#include <bits/stdc++.h>
#define ll long long
#define endl "\n"
#define KUI ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;
struct jgt
{
    int go;
    int d;
};
const int con = 1e5 + 4;
const int inf = 2139062143;
int n, m, k;
bool vis[con];
int dis[con];
vector<struct jgt> v[con];
bool BellmanFord()
{
    dis[1] = 0;  // 更新根节点距离;
    bool bj = 1; // 标记过程中是否发生更新节点的行为;
    for (int i = 1; i <= n; i++)
    {
        bj = 1;                      // 初始化标记;
        for (int j = 1; j <= n; j++) // n次遍历整个图;
        {
            if (dis[j] == inf) // 如果该节点距离仍为最大值,说明没有走到该节点,跳过该节点;
            {
                continue;
            }
            for (auto &x : v[j])
            {
                if (dis[x.go] > dis[j] + x.d)
                {
                    dis[x.go] = dis[j] + x.d;
                    bj = 0; // 如果发生了节点距离变化的过程,更新标记;
                }
            }
        }
        if (bj == 1) // 标记没有被更新,说明没有节点的距离发生变化,图的最短距离已经计算完成;
        {
            return bj;
        }
    }
    return bj;
    // 若图无负环,n次遍历必定可以计算完成最短距离,否则即为图中存在负环;
}
void take()
{
    cin >> n >> m;
    memset(dis, 127, sizeof dis); // 将dis赋值为较大数;
    int a1;
    struct jgt ans;
    for (int i = 1; i <= m; i++)
    {
        cin >> a1 >> ans.go >> ans.d;
        v[a1].push_back(ans); // 存图路径;
    }
    bool pd = BellmanFord();
    if (pd == 1) // 如果返回值为1,则代表最终BellmanFord函数没有再更新节点,说明图没有包含负环;
    {
        cout << "无环" << endl;
    }
    else
    {
        cout << "有环" << endl;
    }
    for (int i = 1; i <= n; i++)
    {
        cout << dis[i] << endl;
    }
}
int main()
{
    KUI;
    int t1 = 1;
    // cin >> t1;
    while (t1--)
    {
        take();
    }
    return 0;
}

 利用队列优化Bellman-Ford算法:

因为只有改变过距离的节点,之后的节点的距离才会有变化,

所以,我们只需要队列保存距离改变过的点,从而更新距离发生改变的以后的点;

#include <bits/stdc++.h>
#define ll long long
#define endl "\n"
#define KUI ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;
struct jgt
{
    int go;
    int d;
};
const int con = 1e5 + 4;
const int inf = 2139062143;
int n, m, k;
bool vis[con];
int dis[con], cont[con]; // cont[i]记录到达i节点经历的边;
vector<struct jgt> v[con];
queue<int> q;
bool BellmanFord()
{
    dis[1] = 0; // 更新根节点距离;
    vis[1] = 1;
    q.push(1); // 入队根节点,标记根节点;
    while (q.size() > 0)
    {
        int ans = q.front();
        vis[ans] = 0;
        q.pop(); // 节点出队,取消标记节点;
        for (auto &x : v[ans])
        {
            if (dis[x.go] > dis[ans] + x.d)
            {
                dis[x.go] = dis[ans] + x.d;
                if (vis[x.go] == 0) // 节点不在队中则入队,如果节点在队中则不需要入队;
                {                   // 如果节点已经在队中,那么距离总会更新;
                    q.push(x.go);
                    vis[x.go] = 1;
                }
                cont[x.go] = cont[ans] + 1;
                if (cont[x.go] >= n) // 如果到达该节点的边数大于等于节点数,说明有节点重复走过了,存在负环;
                {
                    return 0;
                }
            }
        }
    }
    return 1;
    // 若图无负环,n次遍历必定可以计算完成最短距离,否则即为图中存在负环;
}
void take()
{
    cin >> n >> m;
    memset(dis, 127, sizeof dis); // 将dis赋值为较大数;
    int a1;
    struct jgt ans;
    for (int i = 1; i <= m; i++)
    {
        cin >> a1 >> ans.go >> ans.d;
        v[a1].push_back(ans); // 存图路径;
    }
    bool pd = BellmanFord();
    if (pd == 1) // 如果返回值为1,则代表最终BellmanFord函数没有再更新节点,说明图没有包含负环;
    {
        cout << "无环" << endl;
    }
    else
    {
        cout << "有环" << endl;
    }
    for (int i = 1; i <= n; i++)
    {
        cout << dis[i] << endl;
    }
}
int main()
{
    KUI;
    int t1 = 1;
    // cin >> t1;
    while (t1--)
    {
        take();
    }
    return 0;
}

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Bellman-Ford算法是一种用于图中寻找最短路径的算法,可以处理带有负权边的情况。下是一个用C++实现Bellman-Ford算法的示例代码: ```cpp #include <iostream> #include <vector> #include <limits> #define INF std::numeric_limits<int>::max() struct Edge { int from; int to; int weight; }; void bellmanFord(std::vector<Edge>& edges, int n, int start, std::vector<int>& dist) { dist.resize(n, INF); dist[start] = 0; for (int i = 0; i < n - 1; ++i) { for (const auto& edge : edges) { int u = edge.from; int v = edge.to; int weight = edge.weight; if (dist[u] != INF && dist[u] + weight < dist[v]) { dist[v] = dist[u] + weight; } } } // 检测是否存在负权环 for (const auto& edge : edges) { int u = edge.from; int v = edge.to; int weight = edge.weight; if (dist[u] != INF && dist[u] + weight < dist[v]) { std::cout << "图中存在负权环" << std::endl; return; } } } int main() { int n = 5; // 图中节点的数量 std::vector<Edge> edges; // 添加图的边 edges.push_back({0, 1, 6}); edges.push_back({0, 3, 7}); edges.push_back({1, 2, 5}); edges.push_back({1, 3, 8}); edges.push_back({1, 4, -4}); edges.push_back({2, 1, -2}); edges.push_back({3, 2, -3}); edges.push_back({3, 4, 9}); edges.push_back({4, 0, 2}); edges.push_back({4, 2, 7}); int start = 0; // 起始节点 std::vector<int> dist; bellmanFord(edges, n, start, dist); // 输出最短路径 for (int i = 0; i < n; ++i) { std::cout << "从节点" << start << "到节点" << i << "的最短距离为:" << dist[i] << std::endl; } return 0; } ``` 这段代码实现了Bellman-Ford算法,并输出了从指定起始节点到每个节点的最短距离。你可以根据自己的需求修改图的边和起始节点来运行并验证算法的正确性。注意,如果图中存在负权环,算法将无法给出正确结果,并会输出存在负权环的提示。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值