Bellman-Ford && SPFA

Bellman-Ford

Bellman-Ford算法基于边求解最短路,解决单源最短路问题,可以处理含负权边的图,判断是否存在负环。


图上的一条最短路径不超过V-1条边,每次遍历在前一次的基础上操作,是一个迭代松弛的过程。可以把由源点出发到各点的最短路径表示成一棵树,外层的每一次遍历操作就是将树的深度加深一层,内层遍历边执行松弛操作。(松弛:dist[i] = min( dist[i], dist[j]+g[j][i] ))

步骤:
1、将dis(源点到各点的距离)t初始化为INF,dist[source] = 0;
2、外层循环V-1次,内层循环对当边的起点和当前边的终点进行松弛操作(最多执行V-1次)
3、进行第V次操作,如果仍出现有dist更新,则存在负环。
( 时间复杂度为O(V*E) )

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 100 + 10;
const int INF = 0x3f3f3f3f;

struct edge{int from, to, cost;};
edge e[maxn];
int n, m;
int d[maxn];
int path[maxn];

bool Bellman_Ford(int s)
{
    for(int i = 0; i < n; i++) d[i] = INF;
    d[s] = 0;
    for(int k = 0; k < n - 1; k++){
        for(int i = 0; i < m; i++){
            if(d[e[i].from] != INF && d[e[i].from] + e[i].cost < d[e[i].to]){
                d[e[i].to] = d[e[i].from] + e[i].cost;
                path[e[i].to] = e[i].from;
            }
        }
    }

    for(int i = 0; i < m; i++)
        if(d[e[i].from] + e[i].cost < d[e[i].to])
            return true;
    return false;
}

int main()
{
    int s;
    while(~scanf("%d%d%d", &n, &m, &s)){
        for(int i = 0; i < m; i++)
            scanf("%d%d%d", &e[i].from, &e[i].to, &e[i].cost);

        if(Bellman_Ford(s)) printf("Exist negative loop\n");
        else{
            for(int i = 0; i < n; i++) printf("%d ", d[i]);printf("\n");
        }
    }
    return 0;
}

SPFA

SPFA是对Bellman-Ford算法的优化。这里采用队列优化。
将存储结构优化为邻接表,使用队列操作代替外层所有点循环V-1次。判断负环的条件也就变为当某顶点进入队列超过V次,存在负环。


步骤:
1、初始化dist为INF,dist[source] = 0,源点source入队(isque标记点是否在队列内,vis统计点在队列中出现的次数)
2、当队列非空,每次取出队首元素,对于每一个队首所在的点与它可达的点进行松弛操作。若dist有更新,则将判断可达的点是否在队列内,若不在队列内,入队并统计出现次数。
3、当2中发现某一点出现此处大于V,则存在负环。
(期望时间复杂度为O(KE)、K为常数 ,最坏情况复杂度O(VE),可通过SLF和LLL策略对在队列中的插入方式进行优化)


这里采用了链式前向星的数据结构,相当于邻接表。
cnt是输入顺序的编号,head是当前以点x为起点的第一条边的编号,e.next是与当前边同起点的下一条边的位置

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int maxn = 100 + 10;
const int INF = 0x3f3f3f3f;

struct edge{int to, next, cost;};
edge e[maxn];
int head[maxn];
int dist[maxn];
int n, m, cnt;

void add_edge(int x, int y, int w)
{
    e[cnt].to = y;
    e[cnt].next = head[x];
    e[cnt].cost = w;
    head[x] = cnt;
    cnt++;
}

bool spfa(int s)
{
    for(int i = 0; i < n; i++) dist[i] = INF;
    dist[s] = 0;
    int vis[maxn]; memset(vis, 0, sizeof(vis));
    bool inque[maxn]; memset(inque, false, sizeof(inque));
    queue<int> q; q.push(s); vis[s]++; inque[s] = true;
    while(!q.empty()){
        int cur = q.front(); q.pop(); inque[cur] = false;
        for(int i = head[cur]; ~i; i = e[i].next){
            if(dist[e[i].to] > dist[cur] + e[i].cost){
                dist[e[i].to] = dist[cur] + e[i].cost;
                if(!inque[e[i].to]){
                    q.push(e[i].to); inque[e[i].to] = true;
                    if(++vis[e[i].to] > n) return false;
                }

            }
        }
    }
    return true;
}

int main()
{
    int s; cnt =0;
    while(~scanf("%d%d%d", &n, &m, &s)){
        memset(head, -1, sizeof(head));
        for(int i = 0; i < m; i++){
            int x, y, w;
            scanf("%d%d%d", &x, &y, &w);
            add_edge(x, y, w);
        }

        if(!spfa(s)) printf("Exist negative loop!");
        else{
            for(int i = 0; i < n; i++)
                printf("%d ",dist[i]);
            printf("\n");
        }
    }
    return 0;
}

posted @ 2017-07-04 09:51  />.<\  阅读(133)  评论(0编辑  收藏  举报