ZOJ 3946 Highway Project(最小费用最短路,优先队列优化dijkstra)

来源:互联网 发布:知巳手机 编辑:程序博客网 时间:2024/06/06 02:41

ZOJ 3946 Highway Project

Edward, the emperor of the Marjar Empire, wants to build some bidirectional highways so that he can reach other cities from the capital as fast as possible. Thus, he proposed the highway project.
The Marjar Empire has N cities (including the capital), indexed from 0 to N - 1 (the capital is 0) and there are M highways can be built. Building the i-th highway costs Ci dollars. It takes Di minutes to travel between city Xi and Yi on the i-th highway.
Edward wants to find a construction plan with minimal total time needed to reach other cities from the capital, i.e. the sum of minimal time needed to travel from the capital to city i (1 ≤ i ≤ N). Among all feasible plans, Edward wants to select the plan with minimal cost. Please help him to finish this task.
**Input**
There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:
The first contains two integers N, M (1 ≤ N, M ≤ 105).
Then followed by M lines, each line contains four integers Xi, Yi, Di, Ci (0 ≤ Xi, Yi < N, 0 < Di, Ci < 105).
**Output**
For each test case, output two integers indicating the minimal total time and the minimal cost for the highway project when the total time is minimized.
**Sample Input**
2
4 5
0 3 1 1
0 1 1 1
0 2 10 10
2 1 1 1
2 3 1 2
4 5
0 3 1 1
0 1 1 1
0 2 10 10
2 1 2 1
2 3 1 2
**Sample Output**
4 3
4 4
题意:有N点,M边,已知每条边修建所需费用和走过所需时间,求使从0出发分别向其他点走的总时间最小的道路修建方案,在总时间相同的情况下,选择所需总费用最小的方案。输出总时间和总费用。
乍看像是最小生成树,仔细一看才发现是单源最短路问题,附加了一个费用最小的判断。
直接用dijkstra会超时,需要用优先队列优化一下;N比较大,需要用邻接表存储边。
关于锁定最短最小费用边,我是这样操作的:
记录每个点被访问时所经过的最后一条边,并在松弛操作中,对于每条不改变改点最短距离的边,将其费用与先前存储的边的费用比较,取费用较小的一条边作为新的最后访问边。
这段操作代码如下:

for(k=head[n];k!=-1;k=edge[k].next){      m=edge[k].to;      if(!vis[m]&&d[m]>d[n]+edge[k].t){          d[m]=d[n]+edge[k].t;          pre[m]=k;          in.num=m;          in.dis=d[m];          Q.push(in);          continue;      }      if(d[m]==d[n]+edge[k].t){          if(edge[k].w<edge[pre[m]].w){               pre[m]=k;          }      }}

这道题还有一些需要注意的地方:

1)数据范围,对最短距离的存储,数据最大可达1e10,需要用long long

2)初始化最短距离数组的INF,需要define为1e10

在这道题wa了一下午,直接导致了我们队模拟训练成功打铁,最后发现是输出的%I64d出了问题…改成cout就一发入魂了

这个故事告诉我们,输出量小的时候,就不要纠结什么%lld,什么%I64d了,用cout还是很好的…

对于编译一直有很多不解的地方,比如为什么有的题用G++会TLE,会WA,用C++就能A,有的题则是G++能A,还有什么时候用%I64d,什么时候用%lld,都不太了解,是时候补习一下了。

以及对于优先队列的使用还不是很熟练,不会设置先出小的优先队列。补习。

代码如下:

#include<cstdio>#include<cstring>#define INF 1e10+5#include<iostream>#include<queue>#include<algorithm>#define maxn 100005using namespace std;int top;int N,M;typedef struct{    int to,w,next,t;}edgenode;edgenode edge[maxn*2];typedef struct{    int num,dis;}node;int head[maxn];bool vis[maxn];bool road[maxn];long long d[maxn];int pre[maxn];bool operator<(node a,node b){    if(a.dis==b.dis)        return a.num>b.num;    return a.dis>b.dis;//先出小}int add(int s,int e,int t,int w){    edge[top].to=e;    edge[top].w=w;    edge[top].next=head[s];    edge[top].t=t;    head[s]=top++;    return 0;}int main(){    int i,k,T;    int s,e,w,t,n,m;    long long sumt,sumc;    node in,out;    scanf("%d",&T);    while(T--){        top=1;        sumc=0;        sumt=0;        scanf("%d %d",&N,&M);        memset(head,-1,sizeof(head));        memset(road,false,sizeof(road));        memset(pre,-1,sizeof(pre));        memset(vis,false,sizeof(vis));        for(i=1;i<=M;i++){            scanf("%d %d %d %d",&s,&e,&t,&w);            add(s,e,t,w);            add(e,s,t,w);        }        for(i=0;i<N;i++){            d[i]=INF;        }//初始化        d[0]=0;        in.num=0;        in.dis=0;        priority_queue<node> Q;        Q.push(in);        while(!Q.empty()){            out=Q.top();            Q.pop();            n=out.num;            vis[n]=true;            for(k=head[n];k!=-1;k=edge[k].next){                m=edge[k].to;                if(!vis[m]&&d[m]>d[n]+edge[k].t){                    d[m]=d[n]+edge[k].t;                    pre[m]=k;                    in.num=m;                    in.dis=d[m];                    Q.push(in);                    continue;                }                if(d[m]==d[n]+edge[k].t){                    if(edge[k].w<edge[pre[m]].w){                        pre[m]=k;                    }                }            }        }        for(i=0;i<N;i++){            sumt+=d[i];        }        cout<<sumt;        cout<<' ';        for(i=1;i<N;i++){            sumc+=edge[pre[i]].w;        }        cout<<sumc<<endl;    }    return 0;}


0 0