当前位置: 代码迷 >> 综合 >> 蓝桥杯 ADV-4 道路和航路 (Bellman_Ford 和 SPFA 算法解题比较 )
  详细解决方案

蓝桥杯 ADV-4 道路和航路 (Bellman_Ford 和 SPFA 算法解题比较 )

热度:52   发布时间:2024-01-21 20:12:19.0

试题 算法提高 道路和航路

提交此题   评测记录  

资源限制

时间限制:1.0s   内存限制:256.0MB

问题描述

农夫约翰正在针对一个新区域的牛奶配送合同进行研究。他打算分发牛奶到T个城镇(标号为1..T),这些城镇通过R条标号为(1..R)的道路和P条标号为(1..P)的航路相连。

每一条公路i或者航路i表示成连接城镇Ai(1<=A_i<=T)和Bi(1<=Bi<=T)代价为Ci。每一条公路,Ci的范围为0<=Ci<=10,000;由于奇怪的运营策略,每一条航路的Ci可能为负的,也就是-10,000<=Ci<=10,000。

每一条公路都是双向的,正向和反向的花费是一样的,都是非负的。

每一条航路都根据输入的Ai和Bi进行从Ai->Bi的单向通行。实际上,如果现在有一条航路是从Ai到Bi的话,那么意味着肯定没有通行方案从Bi回到Ai。

农夫约翰想把他那优良的牛奶从配送中心送到各个城镇,当然希望代价越小越好,你可以帮助他嘛?配送中心位于城镇S中(1<=S<=T)。

输入格式

输入的第一行包含四个用空格隔开的整数T,R,P,S。

接下来R行,描述公路信息,每行包含三个整数,分别表示Ai,Bi和Ci。

接下来P行,描述航路信息,每行包含三个整数,分别表示Ai,Bi和Ci。

输出格式

输出T行,分别表示从城镇S到每个城市的最小花费,如果到不了的话输出NO PATH。

样例输入

6 3 3 4
1 2 5
3 4 5
5 6 10
3 5 -100
4 6 -100
1 3 -10

样例输出

NO PATH
NO PATH
5
0
-95
-100

数据规模与约定

对于20%的数据,T<=100,R<=500,P<=500;

对于30%的数据,R<=1000,R<=10000,P<=3000;

对于100%的数据,1<=T<=25000,1<=R<=50000,1<=P<=50000。

 

解题思路:为了解有负边的最短路径问题(之前所学的Dijkstra 和Floyd 算法 都被免疫了。。。),我特地搜好多算法慕课,终于找到了Bellman_Ford算法(这个算法挺好理解的,就是不断的松弛操作),跑了一下,35分,后面的测试数据运行超时了。看了大佬们的解题思路,要用SPFA算法。等我学了SPFA,再来改进改进。。。未完待续。。。

二更:在B站上学了一波SPFA,立马把代码改了一遍,SPFA,就是BFS的时候加入松弛操作,利用了BFS求最优解的优点。改进后的代码,跑了85分,提升还是很大滴。。。

Bellman_Ford算法代码如下:

#include <iostream>
#include <vector>

using namespace std;

#define  INF 65535
struct Way{
    int u;
    int v;
    int w;
    Way(){    }
    Way(int uu,int vv,int ww):u(uu),v(vv),w(ww){    }
};

int T,R,P,S;
int dist[25010];
vector<Way>        E;


void ReadInput(){
    cin>>T>>R>>P>>S;    
    int A,B,C;
    
    for(int i=0;i<R;i++){
        cin>>A>>B>>C;
        E.push_back(Way(A,B,C));

        E.push_back(Way(B,A,C));

    }
    
    for(int i=0;i<P;i++){
        cin>>A>>B>>C;
        E.push_back(Way(A,B,C));
    }
    
    for(int i=0;i<=T;i++){
        dist[i]=INF;
    }
    
    dist[S]=0;
}

void Bellman_Ford(){
    
    for(int i=1;i<T;i++){
        for(int j=0;j<E.size();j++){
            if( dist[E[j].u]!=INF &&  dist[E[j].u]+E[j].w<dist[E[j].v] ){
                dist[E[j].v]=dist[E[j].u]+E[j].w;
                //cout<<E[j].u<<" -> "<<E[j].v<<" : "<<dist[E[j].v]<<" = "<<dist[E[j].u]<<" + "<<E[j].w<<endl;
            }
        }
    }
    
    for(int i=1;i<=T;i++){
        if(dist[i]==INF)
            cout<<"NO PATH"<<endl;
        else
            cout<<dist[i]<<endl;
    }
    
}

int main(int argc, char** argv) {
    ReadInput();
    Bellman_Ford();

    return 0;
}

 

SPFA算法代码如下:

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

#define  INF 65535

struct Town{
    int v;
    int w;
    Town(){    }
    Town(int vv,int ww):v(vv),w(ww){    }
};

int T,R,P,S;
int dist[25010];
vector<    vector<Town> >    G(25010);
int inq[25010]; 

void init(){
    for(int i=0;i<=T;i++)
        dist[i]=INF;
    for(int i=1;i<=T;i++)
        inq[i]=0;
    dist[S]=0;
}

void ReadInput(){
    cin>>T>>R>>P>>S;    
    int A,B,C;
    
    for(int i=0;i<R;i++){
        cin>>A>>B>>C;
        G[A].push_back(Town(B,C));
        G[B].push_back(Town(A,C));
        
    }
    
    for(int i=0;i<P;i++){
        cin>>A>>B>>C;
        G[A].push_back(Town(B,C));    
    }
    
    init();
}

void SPFA(){
    queue<int>    Q;
    Q.push(S);
    inq[S]=1;
    while(!Q.empty()){
        int now=Q.front();
        Q.pop();
        inq[now]=0;
        for(int i=0;i<G[now].size();i++){
            int v=G[now][i].v;
            if(dist[v]>dist[now]+G[now][i].w){
                dist[v]=dist[now]+G[now][i].w;
                if(inq[v]==1)
                    continue;
                inq[v]=1;
                Q.push(v);
            }
        }
        
    }
    
    for(int i=1;i<=T;i++){
        if(dist[i]==INF)
            cout<<"NO PATH"<<endl;
        else
            cout<<dist[i]<<endl;
    }
    
    
}
int main(int argc, char** argv) {
    ReadInput();
    SPFA();

    return 0;
}