最短路最新心得

如果,上面的图,如果用dij算法,那么dist[4] = 4,  是得不到正确的结果的, 这个因为dist[3]先被确定是最小,然后用来更新dist[4]

但是存在负权,使得dist[3]更小,但是我们已经把结点3标记为不可用了(vis[3] = true), 所以存在错误

如何使得使得结点3可用呢? 我们把判断的条件给改一下,如果结点u出队列之后,其权值为dist[u] 来得小, 那么就可以用它来更新其他的定点

这样子,每个结点都可以多次入队列, 使得dij可以处理负权

 1 int dij(int x, int y, int n)
 2 {
 3     for (int i = 1; i <= n; ++i)
 4         dist[i] = INF;
 5     priority_queue<Edge> q;
 6     Edge cur, tmp;
 7     cur.dist = dist[x] = 0;
 8     cur.v = x;
 9     q.push(cur);
10     while (!q.empty())
11     {
12         cur = q.top(); q.pop();
13         int u = cur.v;
14         if (dist[u] < cur.dist)//如果cur.dist < dist[u], 那么可以继续更新其他顶点, 代替了条件vis[u]
15             continue;
16         for (int i = 0; i < g[u].size(); ++i)
17         {
18             int v = g[u][i].v;
19             if (dist[v] > dist[u] + g[u][i].dist)
20             {
21                 tmp.dist = dist[v] = dist[u]+ g[u][i].dist;
22                 tmp.v = v;
23                 q.push(tmp);
24             }
25         }
26     }
27     return dist[y];
28 }

bellaman__ford算法的无用操作

void relax(int u, int v,double weight)
{
    if(dist[v] > dist[u] + weight)
        dist[v] = dist[u] + weight;
}
bool bellman_ford(int n, int m)
{
    int i,j;
    for(i=0; i<n-1; ++i)//n-1循环
        for(j=0; j<m; ++j)//枚举所有的边去松弛最短路径
        {
            relax(g[j].u,g[j].v,g[j].weight);
        }
    bool flag = false;
    for(i=0; i<m; ++i)
        if(dist[g[i].v] > dist[g[i].u] + g[i].weight)
        {
            flag = true;
            break;
        }
    return flag;
}

如上述代码所示, 我们枚举每条边, 看看能不能松弛最短路径, 但这么做,其实做了很多的无用功, 比如说如果 dist[g[j]] 没有被松弛过或者松弛不成功,

那么relax(g[j].u,g[j].v,g[j].weight) 做的就是无用功

对于这个, 我们可以用队列来优化bellman算法, (上交的大神发明的,时间复杂度是O(ke),k是常数,虽然后来被证实它证明的k是常数是错误的)

如果结点u被更新过了, 那么才可以用它来更新它所指向的定点 

int spfa(int x, int y, int n)
{
    for (int i = 1; i <= n; ++i)
        dist[i] = INF;
    queue<int> q;
    q.push(x);
    dist[x] = 0;
    while (!q.empty())
    {
        int u = q.front(); q.pop();
        vis[u] = false;
        for (int i = 0; i < g[u].size(); ++i)
        {
            int v = g[u][i].v;
            if (dist[v] > dist[u] + g[u][i].dist)
            {
                dist[v] = dist[u] + g[u][i].dist;//能更新就更新
                if (!vis[v])//如果结点在队里里面,就不用重复入队了
                {
                    q.push(v);
                    vis[v] = true;
                }
            }
        }
    }
    return dist[y];
}
原文地址:https://www.cnblogs.com/justPassBy/p/4646413.html