校內測T2 李白坐地鐵(Dijkstra模板題)
題目背景
濟南地鐵已經開通了R1和R3線。
小李白也興奮地在高考前去體驗了一下,發現可以用公交卡刷卡並且車站文化氛圍很濃厚qwq
題目描述
小李白來到了一個大城市,他住在了一個五星級酒店,他規劃著接下來的幾天內在大城市的城區內坐地鐵遊玩景點。這個城市有著完善的地鐵網路。但是非常不幸的是,地鐵全是單向行駛的。
小李白每天都要坐地鐵去一個景點遊玩,然後傍晚時分坐地鐵回來。第一天去編號為2的景點,第二天去編號為3的景點......小李白要去過所有的景點才會罷休qwq。我們設每一個地鐵站點都是一個景點。對於每一天的景點,小李白都想知道如何坐地鐵才能使得來回總時間合最小。他會給你整個城市的地鐵路線和站點圖,並且向你求助這個問題。
有n個站點,m個站點間的地鐵線路。小李白所住的酒店需要耗時時間d才能到地鐵站1,也就是從站點1開始坐地鐵。
資料會給你每對站點之間的路線長度w.
輸入格式
第1行:n,m,d
第2到m+1行,u,v,w,表示從站點u到站點v有一條長度為w的地鐵路線。
輸出格式
小李白每天在路上耗時的和sum。 為了更方便的解釋,我們設每天在路上花費的時間為time[i],小李白要去的景點數為k。
那麼答案就是∑i=1ktime[i]。
輸入輸出樣例
輸入 #15 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
83
說明/提示
對於30%的資料,n=1
對於100%的資料:
n⩽1000
m⩽100000
w⩽200
d⩽1500000
均為整數。
小李白遠遠不如他的朋友ych強,所以只能結合高考前放假經歷隨便出了個題
注意空間時間限制qwq。
思路:
參觀每一個景點都要從一號車站過去再回來,所以很明顯是一個單源最短路,因為沒有負邊權,所以可以使用比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 head1[x]=tot1;//鏈式前向星結構,把新加入的邊放到與x相連線的第一條邊 Next1[tot1]=head1[x];//新加入的邊的下一條邊是原本與x相連的第一條邊 } void add2(long long x,long long y,long long z){ ver2[++tot2]=y; edge2[tot2]=z; head2[x]=tot2; Next2[tot2]=head2[x]; } 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\n",ans); return 0; }