EOJ 1848 您是ACM吗? 用二叉堆优化dijkstra + spfa算法的学习

EOJ 1848 你是ACM吗? 用二叉堆优化dijkstra + spfa算法的学习

Description 

随着中国经济的腾飞,中国的物流产业迎来了发展的春天。特别是在上海这样一个拥有广阔国内腹地的国际化大都市,物流业以空前的速度膨胀。
当然是大蛋糕就会吸引许多馋嘴猫,馋嘴猫多了就会有残酷的竞争。当大量资金流入物流产业时,KOP 集团为了稳坐在国内物流业的第一把交椅,决定对现行的运输方案进行改良,以减少自己的成本同时使其它竞争者知难而退。
作为世界100强的KOP集团当然知道要找到最优运输方案,肯定得靠数学和算法很好的软件工程师,于是他们理所当然地找到华东师范大学软件学院。决定通过赞助一场程序比赛来找出最优秀的工程师( ACM : Ace Coding Man )。
比赛只有一道题目,是运输线路的简单抽象,题意如下:
SH 市有N个运输中转点(简单标示为 1,2,3,....,N),中转点之间可能有一条运输线路,这条线路有一个特殊的地方就是从A 到B点需要耗费 c1 个单位的查克拉(SH市的货币单位),但从B到A可能需要 c2 个查克拉。当然c1不一定等于c2,也能从A到B之后就不能从B返回A了。你可以理解为这些线路是“单向”的。线路总共有 M 条。每天有N-1辆车从KOP集团总部(这里假设就是标号为1的中转点),出发,分别发往N-1个剩下的中转点,然后当天再从所到达的中转点返回。你的任务就是要求出一天的最小耗费。

Input 

第一行为 C ,表示有C个测试用列。接下来分别为C个测试用列。
每个测试用例的格式如下:
第一行为两个整数,N,M ,分别表示有N个中转点和M条道路。( 1=< N, M <=1000000 .);
紧接着的M 行每行有三个值 A B c; 分别表示从中转点A到中转点B需要耗费 c 个单位的查克拉。 ( 0<= c <= 1000000000 ).

Output 

你的输出应该包括C行,每行输出一个值,对应于相应的用列的最少耗费。

Sample Input 

2
2 2
1 2 13
2 1 33
4 6
1 2 10
2 1 60
1 3 20
3 4 10
2 4 5
4 1 50

Sample Output 

46
210

 

 

题目分析:给出一个有向图图,保证是联通的,求1到所有点的最小距离和加上所有点到1的最小距离和的总和,只需要分别把边正向存储和反向存储一次,做两次单源最短路径算法即可。因为点数量很多,所以要用邻接表的方法来存储图,同时使用邻接图还可以减少枚举数量,提高效率。可用1.二叉堆优化dijkstra的方法和2.spfa算法完成。

         方法一:二叉堆优化dijkstra.将每个点当前到源点的最短距离放入堆中,每次就可在logn时间复杂度取出,每次再把利用新加入的点更新的距离放入堆中,为区别一个点是否加入源点所在集合,加一个标记数组即可,整体思路和普通dijkstra相近。AC代码见下。

         方法二:spfa算法。其复杂度为          O(KE),E为边数,K为平均每个点更新次数,通常小于2。算法主要思想:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前最短路径值对u点所指向的结点v进行松弛操作,如果v点的最短路径值有所调整,且 v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。因为每次将点放入队尾,都是经过松弛操作达到的。换言之,每次的优化将会有某个点v的最短路径估计值d[v]变小。所以算法的执行会使d越来越小。假定图中不存在负权回路,所以每个结点都有最短路径值。因此,算法不会无限执行下去,随着d值的逐渐变小,直到到达最短路径值时,算法结束,这时的最短路径估计值就是对应结点的最短路径值。

         对比:方法一是用堆优化dijkstra每次寻找最短估计距离的效率,标记数组标记一个点是否已经加入源点所在集合,即已经最优。方法二是不断更新每个点的最短距离估计值,直到不能更新位置,思想是不断逼近最优解。同样需要一个标记数组标记一个点是否已在队中(防止在队中的点再次入队已提高效率)。另外dijkstra不能处理带负权的图,spfa可以,但需要判断是否存在权为负的回路(即是否有点入队n次)。

 

Dijkstra AC代码:

 

#include <iostream>

#include <cstdio>

#include <cstring>

#include <cmath>

#include <string>

#include <vector>

#include <map>

#include <algorithm>

#include <queue>

using namespace std;

const int MAXN=1000001;

const long long INF=1000000001;

struct Edge{

   int from,to,dist;

};

int n,m;

vector<Edge> edge[2];

vector<int> g[2][MAXN];

long long d[2][MAXN];

bool vis[MAXN];

 

struct Node{

   long long d;

   int u;

   bool operator<(const Node& x)const{

       return d>x.d;

    }

};

 

void addedge(int from,int to,int dis,intk){

   edge[k].push_back((Edge){from,to,dis});

   int t=edge[k].size();

   g[k][from].push_back(t-1);

}

void dijkstra(int s,int k){

   priority_queue<Node> q;

   memset(vis,false,sizeof(vis));

   for(int i=1;i<=n;++i) d[k][i]=INF;

   d[k][s]=0;

   q.push((Node){0,s});

   while(!q.empty()){

        Node x=q.top();q.pop();

       int u=x.u;

       if(vis[u]) continue;

       vis[u]=true;

       for(int i=0;i<g[k][u].size();++i){

           Edge &e=edge[k][g[k][u][i]];

           if(d[k][u]+e.dist<d[k][e.to]){

                d[k][e.to]=d[k][u]+e.dist;

               q.push((Node){d[k][e.to],e.to});

           }

       }

    }

}

 

int main()

{

   int t;

   scanf("%d",&t);

   while(t--){

       scanf("%d%d",&n,&m);

       edge[1].clear();edge[0].clear();

       for(int i=0;i<=n;++i){

           g[0][i].clear();

           g[1][i].clear();

       }

       for(int i=0;i<m;++i){

           int a,b,c;

           scanf("%d%d%d",&a,&b,&c);

           addedge(a,b,c,0);

           addedge(b,a,c,1);

       }

       dijkstra(1,0);

        dijkstra(1,1);

       long long ans=0;

       for(int i=2;i<=n;++i)

           ans+=d[0][i]+d[1][i];

       printf("%I64d\n",ans);

    }

   return 0;

}

 

 

 

 

 

Spfa AC代码:

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>


using namespace std;
const int INF=1e9+5;
const int MAXN=1000002;
struct Edge{
    int from,to,dis;
};
bool vis[MAXN];
long long d[2][MAXN];
int n,m;
vector<Edge> edge[2];
vector<int> g[2][MAXN];


void add(int from,int to,int dis,int k){
    edge[k].push_back((Edge){from,to,dis});
    int x=edge[k].size();
    g[k][from].push_back(x-1);
}


void spfa(int k){
    memset(vis,false,sizeof(vis));
    vis[1]=true;
    d[k][1]=0;
    queue<int> q;
    q.push(1);
    while(!q.empty()){
        int u=q.front();q.pop();
        vis[u]=false;
        for(int i=0;i<g[k][u].size();++i){
            Edge &e=edge[k][g[k][u][i]];
            if(d[k][e.to]>d[k][u]+e.dis){
                d[k][e.to]=d[k][u]+e.dis;
                if(!vis[e.to]){
                    vis[e.to]=true;
                    q.push(e.to);
                }
            }
        }
    }
}


int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        for(int i=0;i<=n;++i){
            g[0][i].clear();g[1][i].clear();
            d[0][i]=INF;d[1][i]=INF;
        }
        edge[0].clear();edge[1].clear();
        for(int i=0;i<m;++i){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,c,0);
            add(b,a,c,1);
        }
        spfa(0);
        spfa(1);
        long long ans=0;
        for(int i=2;i<=n;++i){
            ans+=d[0][i]+d[1][i];
        }
        printf("%I64d\n",ans);
    }
    return 0;
}