zoukankan      html  css  js  c++  java
  • (模板)hdoj2544(最短路--bellman-ford算法&&spfa算法)

    题目链接:https://vjudge.net/problem/HDU-2544

    题意:给n个点,m条边,求点1到点n的最短路。

    思路:

      今天学了下bellman_ford,抄抄模板。dijkstra算法和该算法都是单源最短路径算法,但是dij不能适用含负权边的图。而bellman-ford算法适用于负权边,原理是进行n-1次松弛操作,每次都要对m条边进行松弛,所以算法复杂度是O(mn),比dijkstra要高。如果n-1次操作之后还能进行松弛,说明存在负环。

    AC code:

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    
    const int maxn=105;
    const int maxm=10005;
    const int inf=0x3f3f3f3f;
    int n,m,dis[maxn],cnt,loop;
    
    struct node{
        int u,v,w;
    }edge[maxm<<1];
    
    void adde(int u,int v,int w){
        edge[++cnt].v=v;
        edge[cnt].u=u;
        edge[cnt].w=w;
    }
    
    void bellman_ford(int s){
        for(int i=1;i<=n;++i)
            dis[i]=inf;
        dis[s]=0;
        //n-1次松弛    
        for(int i=1;i<n;++i){
            bool ok=0;
            for(int j=1;j<=cnt;++j){
                int u=edge[j].u,v=edge[j].v,w=edge[j].w;
                if(dis[v]>dis[u]+w){
                    dis[v]=dis[u]+w;
                    ok=1;
                }
            }
            if(!ok) break;
        }
        //loop=1说明存在负环
        for(int i=1;i<=cnt;++i){
            int u=edge[i].u,v=edge[i].v,w=edge[i].w;
            if(dis[v]>dis[u]+w){
                loop=1;
                break;
            }
        }
    }
    
    int main(){
        while(scanf("%d%d",&n,&m),n||m){
            cnt=0,loop=0;
            for(int i=1;i<=m;++i){
                int u,v,w;
                scanf("%d%d%d",&u,&v,&w);
                adde(u,v,w);
                adde(v,u,w);
            }
            bellman_ford(1);
            printf("%d
    ",dis[n]);
        }
        return 0;
    }

    思路2:

    spfa是bellman-ford的队列优化版本,使用条件一样,vis标记该点是否在队列中,被更新的结点如果不在队列中要重新入队。也可以用来求是否存在负环,用updcnt数组记录每个结点更新次数,如果==n说明更新了n次,即存在负环。

    然后说一下spfa的时间复杂度是O(km),k为常数,但最坏情况是O(mn),比较毒瘤,慎用!

    下面的代码是luoguP3385,判负环。

    AC code:

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    using namespace std;
    
    const int maxn=2005;
    const int maxm=6005;
    const int inf=0x3f3f3f3f;
    int T,n,m,head[maxn],vis[maxn],cnt,upd[maxn],dis[maxn];
    
    struct node{
        int v,w,nex;
    }edge[maxm];
    
    void adde(int u,int v,int w){
        edge[++cnt].v=v;
        edge[cnt].w=w;
        edge[cnt].nex=head[u];
        head[u]=cnt;
    }
    
    bool spfa(){
        for(int i=1;i<=n;++i)
            vis[i]=0,upd[i]=0,dis[i]=inf;
        queue<int> que;
        que.push(1);
        vis[1]=1,dis[1]=0,++upd[1];
        while(!que.empty()){
            int u=que.front();que.pop();
            vis[u]=0;
            for(int i=head[u];i;i=edge[i].nex){
                int v=edge[i].v,w=edge[i].w;
                if(dis[v]>dis[u]+w){
                    dis[v]=dis[u]+w;
                    if(!vis[v]){
                        ++upd[v];
                        if(upd[v]>n) return true;
                        vis[v]=1;
                        que.push(v);
                    }
                }
            }
        }
        return false;
    }
    
    int main(){
        scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&m);
            for(int i=1;i<=n;++i)
                head[i]=0;
            cnt=0;
            for(int i=1;i<=m;++i){
                int u,v,w;
                scanf("%d%d%d",&u,&v,&w);
                if(w<0){
                    adde(u,v,w);
                }
                else{
                    adde(u,v,w);
                    adde(v,u,w);
                }
            }
            if(spfa()) printf("YE5
    ");
            else printf("N0
    ");
        }
        return 0;
    }

      拓展:

      bellman-ford算法还能用来求最长路或者是判断正环,只用改下松弛条件即可。改变dis数组的定义为从源点到其它结点最长路径的长度,初始化为0,松弛时,如果dis[v]<dis[u]+w,则更新,其它操作一样。入poj1860。

    AC code:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    
    const int maxn=205;
    int n,m,s,head[maxn],vis[maxn],upd[maxn],cnt;
    double V,dis[maxn];
    
    struct node{
        int v,nex;
        double r,c;
    }edge[maxn<<1];
    
    void adde(int u,int v,double r,double c){    
        edge[++cnt].v=v;
        edge[cnt].r=r;
        edge[cnt].c=c;
        edge[cnt].nex=head[u];
        head[u]=cnt;
    }
    
    bool spfa(int s,double V){
        for(int i=1;i<=n;++i) dis[i]=0,vis[i]=0,upd[i]=0;
        dis[s]=V,vis[s]=1;
        queue<int> que;
        que.push(s);
        ++upd[s];
        while(!que.empty()){
            int u=que.front();que.pop();
            vis[u]=0;
            for(int i=head[u];i;i=edge[i].nex){
                int v=edge[i].v;
                double r=edge[i].r,c=edge[i].c;
                if(dis[v]<(dis[u]-c)*r){
                    dis[v]=(dis[u]-c)*r;
                    if(!vis[v]){
                        ++upd[v];
                        if(upd[v]>n) return true;
                        que.push(v);
                        vis[v]=1;
                    }
                }
            }
        }
        if(dis[s]>V) return true;
        return false;
    }
    
    int main(){
        scanf("%d%d%d%lf",&n,&m,&s,&V);
        for(int i=1;i<=n;++i) head[i]=0;
        cnt=0;
        for(int i=1;i<=m;++i){
            int u,v;
            double ruv,cuv,rvu,cvu;
            scanf("%d%d%lf%lf%lf%lf",&u,&v,&ruv,&cuv,&rvu,&cvu);
            adde(u,v,ruv,cuv);
            adde(v,u,rvu,cvu);
        }
        if(spfa(s,V)) printf("YES
    ");
        else printf("NO
    ");
        return 0;
    }
  • 相关阅读:
    《Programming WPF》翻译 第8章 1.动画基础
    一些被遗忘的设计模式
    《Programming WPF》翻译 第4章 数据绑定
    《Programming WPF》翻译 第3章 控件
    《Programming WPF》翻译 第5章 样式和控件模板
    《Programming WPF》翻译 第7章 绘图
    《Programming WPF》翻译 第9章 自定义控件
    《Programming WPF》翻译 第7章 绘图 (2)
    《Programming WPF》翻译 第8章 前言
    关于Debug和Release之本质区别
  • 原文地址:https://www.cnblogs.com/FrankChen831X/p/11849901.html
Copyright © 2011-2022 走看看