校内测T2 李白坐地铁(Dijkstra模板题)

题目背景

济南地铁已经开通了R1和R3线。

小李白也兴奋地在高考前去体验了一下,发现可以用公交卡刷卡并且车站文化氛围很浓厚qwqqwq

题目描述

小李白来到了一个大城市,他住在了一个五星级酒店,他规划着接下来的几天内在大城市的城区内坐地铁游玩景点。这个城市有着完善的地铁网络。但是非常不幸的是,地铁全是单向行驶的。

小李白每天都要坐地铁去一个景点游玩,然后傍晚时分坐地铁回来。第一天去编号为2的景点,第二天去编号为3的景点......小李白要去过所有的景点才会罢休qwqqwq。我们设每一个地铁站点都是一个景点。对于每一天的景点,小李白都想知道如何坐地铁才能使得来回总时间合最小。他会给你整个城市的地铁路线和站点图,并且向你求助这个问题。

nn个站点,mm个站点间的地铁线路。小李白所住的酒店需要耗时时间dd才能到地铁站11,也就是从站点11开始坐地铁。

数据会给你每对站点之间的路线长度ww.

输入格式

第1行:n,m,dn,m,d

2到m+12m+1行,u,v,w,表示从站点u到站点v有一条长度为w的地铁路线。

输出格式

小李白每天在路上耗时的和sumsum。 为了更方便的解释,我们设每天在路上花费的时间为time[i]time[i],小李白要去的景点数为kk

那么答案就是sum_{i=1}^k time[i]i=1ktime[i]

输入输出样例

输入 #1
5 10 0
2 3 5
1 5 5
3 5 6
1 2 8
1 3 8
5 3 4
4 1 8
4 5 3
3 5 6
5 4 2
输出 #1
83

说明/提示

对于30\%30%的数据,n=1n=1

对于100\%100%的数据:

nleqslant1000n1000

mleqslant100000m100000

wleqslant200w200

dleqslant1500000d1500000

均为整数。

小李白远远不如他的朋友ych强,所以只能结合高考前放假经历随便出了个题

注意空间时间限制qwqqwq

qwq_{ych_{Orz}}^{ybr_{csl}^{tcl}}

思路:

参观每一个景点都要从一号车站过去再回来,所以很明显是一个单源最短路,因为没有负边权,所以可以使用比SPFA更快的Dijkstra算法来求单源最短路。但是从各个景点回来的时候应该怎么办呢?通过dalao的题解,我学会了建反边这一个神奇的操作。因为每次从景点回来都是回到一号车站,都是那一个点,所以如果在另一张新图里反向建边,就可以再跑一遍Dijkstra,从而转化为单源最短路(妙啊)

 
代码:
 
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
const int N=1005,M=100005;
long long n,m,k,tot1,tot2,ans;
long long Next1[M],ver1[M],edge1[M],head1[N],d1[N],Next2[M],ver2[M],edge2[M],head2[N],d2[N];//不开longlong见祖宗 
bool v1[N],v2[N];
priority_queue< pair <int,int> >q1;
priority_queue< pair <int,int> >q2;//因为要存两个图,所以什么变量都开两遍 
void add1(long x,long long y,long long z){
    ver1[++tot1]=y;//编号为tot的边的终点是y 
    edge1[tot1]=z;//边权是z 
    Next1[tot1]=head1[x];//新加入的边的下一条边是原本与x相连的第一条边     
    head1[x]=tot1;//链式前向星结构,把新加入的边放到与x相连接的第一条边
}
void add2(long long x,long long y,long long z){
    ver2[++tot2]=y;
    edge2[tot2]=z;
    Next2[tot2]=head2[x];
    head2[x]=tot2;
}
void dijkstra1(long long x){
    memset(d1,0x3f,sizeof(d1));//把起点到所有点的距离都初始化为无穷大,保证能更新 
    memset(v1,0,sizeof(v1));//一开始所有点都没有到过 
    d1[x]=0;//自己到自己的距离当然是0 
    q1.push(make_pair(0,x));//pair的第一维表示点x到起点的距离,第二维表示点的编号x 
    while(q1.size()!=0){//当堆不为空 
        long long x=q1.top().second;//取出第二维点的编号 
        q1.pop();//弹出 
        if(v1[x]==1) continue;//如果这个点已经被访问过了,那么继续循环 
        v1[x]=1;//如果没有访问过那么标记为访问过 
        for(long long i=head1[x];i;i=Next1[i]){//链式前向星遍历
            long long y=ver1[i],z=edge1[i];
            if(d1[y]>d1[x]+z){//松弛操作 
                d1[y]=d1[x]+z;
                q1.push(make_pair(-d1[y],y));//加入堆中,因为STL中提供的是大根堆,我们要用小根堆,所以把距离的相反数存进去排序,就相当于是小根堆了 
            }
        }
    }
}
void dijkstra2(long long x){
    memset(d2,0x3f,sizeof(d2));
    memset(v2,0,sizeof(v2));
    d2[x]=0;
    q2.push(make_pair(0,x));
    while(q2.size()!=0){
        long long x=q2.top().second;
        q2.pop();
        if(v2[x]==1) continue;
        v2[x]=1;
        for(long long i=head2[x];i;i=Next2[i]){ 
            long long y=ver2[i],z=edge2[i];
            if(d2[y]>d2[x]+z){
                d2[y]=d2[x]+z;
                q2.push(make_pair(-d2[y],y));
            }
        }
    }
}
int main()
{
    scanf("%lld%lld%lld",&n,&m,&k);
    for(long long i=1,x,y,z;i<=m;i++){
        scanf("%lld%lld%lld",&x,&y,&z);
        add1(x,y,z);
        add2(y,x,z);//反着建边 
    }
     ans=(n-1)*k*2;//每天从酒店到一号车站所要走的距离 
    dijkstra1(1);
    dijkstra2(1);//跑两遍Dijkstra 
    for(long long i=1;i<=n;i++){
        ans+=d1[i]+d2[i];//把来回的距离和加起来 
    }
    printf("%lld
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/57xmz/p/13288470.html