最短路:Bellman-Ford

16 篇文章 0 订阅
9 篇文章 0 订阅

Bellman-Ford

Bellman-Ford的核心思想是松弛操作,对于边 ( u , v ) (u,v) (u,v),用 d i s t ( u ) dist(u) dist(u) l ( u , v ) l(u,v) l(u,v)的和尝试更新 d i s t ( v ) dist(v) dist(v):

d i s t ( v ) = m i n ( d i s t ( v ) , d i s t ( u ) + l ( u , v ) ) dist(v)=min(dist(v),dist(u)+l(u,v)) dist(v)=min(dist(v),dist(u)+l(u,v))

Bellman-Ford就是不断尝试对图上每一条边进行松弛操作。

进行多次迭代,每进行一次迭代,就对图上所有的边都尝试进行一次松弛操作,当一次迭代中没有点的dist发生改变时,算法停止。

先将除了起始点的距离赋值为无穷大,起始点赋值为0

在每次迭代中,遍历所有的边,尝试用“松弛操作”更新距离数组

int n, m, dist[N], pre[N], c[N];
struct Edge
{
    int x, y, v;
} edge[M];
int shortestpath(int s, int t)
{
    memset(dist, 127, sizeof dist);
    memset(pre, 0, sizeof pre);
    dist[s] = 0;
    while (true)
    {
        bool flag = false;
        for (int i = 1; i <= m; i++)
        {
            int x = edge[i].x;
            int y = edge[i].y;
            int v = edge[i].v;
            if (dist[x] < 1 << 30)
                if (dist[x] + v < dist[y])
                {
                    dist[y] = dist[x] + v;
                    pre[y] = x;
                    flag = true;
                }
        }
        if (!flag)
            break;
    }
    //输出路径
    if (dist[t] < 1 << 30)
    {
        int l = 0;
        for (int i = t; i != s; i = pre[i])
            c[++l] = i;
        c[++l] = s;
        for (int i = l; i; --i)
            printf("%d ", c[i]);
        puts("");
    }
    return dist[t];
}
int n, m;
int dist[N];
struct Edge
{
    int a, b, w;
}edges[M];

int bellman_ford()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    for (int i = 0; i < n; i ++ )
    {
        for (int j = 0; j < m; j ++ )
        {
            int a = edges[j].a, b = edges[j].b, w = edges[j].w;
            if (dist[b] > dist[a] + w)
                dist[b] = dist[a] + w;
        }
    }

    if (dist[n] > 0x3f3f3f3f / 2) return -1;
    return dist[n];
}

Bellman-Ford的优化

队列优化(SPFA)(能被卡到和Bellman-Ford一样的复杂度)

在每一次迭代时,只有在上一次迭代中被更新了距离的点,才有可能去更新其他节点,在每一次迭代时,我们将更新过距离的顶点加入一个队列(如果顶点已经在队列里则不加),在下一次迭代时只需要遍历队列中的顶点连出去的边即可。

typedef pair<int, int> PII;
vector<PII> edge[N];
int n, m, k, dist[M];
bool b[N];
queue<int> q;
void shortestpath(int s, int t)
{
    memset(dist, 127, sizeof dist);
    memset(b, false, sizeof b);
    dist[s] = 0;
    b[s] = true;
    q.push(s);
    while (!q.empty())
    {
        int x = q.front();
        q.pop();
        b[x] = false;
        for (auto i : edge[x])
            if (dist[x] + i.second < dist[i.first])
            {
                dist[i.first] = dist[x] + i.second;
                if (!b[i.first])
                {
                    q.push(i.first);
                    b[i.first] = true;
                }
            }
    }
    if (dist[t] < 1 << 30)
        printf("%d\n", dist[t]);
    else
        printf("-1\n");
}

spfa判断负环

不需要初始化dist数组
原理:如果某条最短路径上有n个点(除了自己),那么加上自己之后一共有n+1个点,由抽屉原理一定有两个点相同,所以存在环。

typedef pair<int, int> PII;
int n;
vector<PII> edge[N];
int dist[N], cnt[N]; 
bool st[N];

// 存在负环返回true,否则返回false。
bool spfa()
{
    queue<int> q;
    for (int i = 1; i <= n; i++)
    {
        q.push(i);
        st[i] = true;
    }
    while (!q.empty())
    {
        int x = q.front();
        q.pop();

        st[x] = false;

        for (auto i : edge[x])
        {
            if (dist[i.first] > dist[x] + i.second)
            {
                dist[i.first] = dist[x] + i.second;
                cnt[i.first] = cnt[x] + 1;
                if (cnt[i.first] >= n)
                    return true;
                if (!st[i.first])
                {
                    q.push(i.first);
                    st[i.first] = true;
                }
            }
        }
    }

    return false;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值