poj3259 Wormholes BellmanFord或SPFA

来源:互联网 发布:佳为软件称软件 编辑:程序博客网 时间:2024/05/16 07:29

今天学了一下BellmanFord算法(它也是一个求单元最短路的算法),它和不使用优先队列的Dijkstra算法复杂度差不多,但是它可以处理带有负边权的图。

算法实现很简单:给定图G=(V, E), |V| = N。 (1)我们对所有边进行N-1次松弛操作可以得到源点到所有点的最短距离。(2)再对所有边进行一次松弛操作,判断是否存在负环,如果存在负环,则从S到所有点的最短距离不存在,否则求解完毕。


这道题题意:给定一些双向边(普通路径),边权为正,再给定一些单向边,边权为负,问是在这个图里否存在一个负环,存在负环就输出YES,否则输出NO。

直接用这个算法就A了。


#include<stdio.h>#include<string.h>const int N = 512;const int M = 6000; //邻接表存双向边要开边数的双倍const int INF = 0x3f3f3f3f;struct Edge {    int v, w;    int next;};int dis[N];int fir[N];Edge edge[M];int cnt, n;void init() {    cnt = 1;    memset(fir, -1, sizeof(fir));}void addEdge(int u, int v, int w) {    edge[cnt].v = v;    edge[cnt].w = w;    edge[cnt].next = fir[u];    fir[u] = cnt++;}bool bellmanFord(int s) {    memset(dis, INF, sizeof(dis));    dis[s] = 0;    for(int i = 1; i < n; i++) {        for(int j = 1; j <= n; j++) {            for(int k = fir[j]; k != -1; k = edge[k].next) {                int v = edge[k].v;                if(dis[v] > dis[j] + edge[k].w)                    dis[v] = dis[j] + edge[k].w;            }        }    }    for(int j = 1; j <= n; j++) {//        printf("[%d]\n", dis[j]);        for(int k = fir[j]; k != -1; k = edge[k].next) {            int v = edge[k].v;            if(dis[v] > dis[j] + edge[k].w)                return true;        }    }    return false;}int main() {    int f;    scanf("%d", &f);    while(f--) {        init();        int m, w;        scanf("%d%d%d", &n, &m, &w);        for(int i = 0; i < m; i++) {            int s, e, t;            scanf("%d%d%d", &s, &e, &t);            addEdge(s, e, t);            addEdge(e, s, t);        }        for(int i = 0; i < w; i++) {            int s, e, t;            scanf("%d%d%d", &s, &e, &t);            addEdge(s, e, -t);        }        if(bellmanFord(1))            printf("YES\n");        else printf("NO\n");    }    return 0;}


Bellman-Ford能解决的通常SPFA都能解决(SPFA是Bellman-Ford的另一种实现方法,故SPFA也是求单元最短路),而且SPFA效率更高。

SPFA的主要步骤:

需要用的存储结构:

邻接表。

队列que<int>。

dis[ u ]:从源点S到u的最短距离。

vis[ u ]:u是否被访问过。

outque[ u ]: u出队的次数。

(1)源点S入队。

(2)访问队首元素,并让其出队,该点出队次数+1,判断该点出队次数是否大于N, 若果大于N则图中存在负环路,否则访问该点的所有连接边,进行松弛操作,如果可以松弛,则更新dis并且把当前顶点入队。

(3)重复(2)直到队列为空。


#include<stdio.h>#include<string.h>#include<queue>using namespace std;const int N = 512;const int M = 6000;const int INF = 0x3f3f3f3f;struct Edge {    int v, w, next;};Edge edge[M];int fir[N];int outque[N];int vis[N], dis[N];int cnt, n;void init() {    cnt = 1;    memset(fir, -1, sizeof(fir));}void addEdge(int u, int v, int w) {    edge[cnt].v = v;    edge[cnt].w = w;    edge[cnt].next = fir[u];    fir[u] = cnt++;}bool SPFA(int s) {    memset(dis, INF, sizeof(dis));    memset(vis, 0, sizeof(vis));    memset(outque, 0, sizeof(outque));    queue<int> que;    dis[s] = 0;    que.push(s);    while(!que.empty()) {        int now = que.front();        outque[now] ++;        if(outque[now] > n) return false;        vis[now] = 0;        que.pop();        for(int i = fir[now]; i != -1; i = edge[i].next) {            int v = edge[i].v;            if(dis[v] > dis[now] + edge[i].w) {                dis[v] = dis[now] + edge[i].w;                vis[v] = 1;                que.push(v);            }        }    }    return true;}int main() {    int f;    scanf("%d", &f);    while(f--) {        int w, m;        init();        scanf("%d%d%d", &n, &m, &w);        for(int i = 0; i < m; i++) {            int u, v, val;            scanf("%d%d%d", &u, &v, &val);            addEdge(u, v, val);            addEdge(v, u, val);        }        for(int i = 0; i < w; i++) {            int u, v, val;            scanf("%d%d%d", &u, &v, &val);            addEdge(u, v, -val);        }        if(!SPFA(1))            printf("YES\n");        else printf("NO\n");    }    return 0;}



0 0
原创粉丝点击