解題歷程:
看到兩邊同時移動,計算最終的相遇時間,我就想到兩邊同時計算各點到起點的最短距離,就是使用dijstra演算法,最後所有節點取兩次計算的最大值,再對所有節點取最小值,就是最終答案了,可是這個思路沒有考慮有馬的情況,思考一番後發現可以多列一個陣列記錄有馬的情況下的行走最短路,然後將有馬和無馬取最小值合併成單源最短路的結果
程式碼:
#include<bits/stdc++.h>
#define ll long long
#define inf 1e18
using namespace std;
void solve() {
int n,m,h;
cin>>n>>m>>h;
vector<vector<pair<ll,ll>>>a(n+1);
vector<int>horse(n+1,0);
for(int i=0;i<h;i++)
{
int l;
cin>>l;
horse[l]=1;
}
for(int i=1;i<=m;i++)
{
ll u,v,w;
cin>>u>>v>>w;
a[u].push_back({v,w});
a[v].push_back({u,w});
}
auto dijstra=[&](int s){
priority_queue<pair<ll,ll>,vector<pair<ll,ll> >,greater<>>pq;//devc++要用greater<ll>
vector<ll>vis(2*n+5,inf);
pq.push({0ll,2*s});
while(!pq.empty()){
ll d=pq.top().first;
ll u=pq.top().second;
pq.pop();
if(vis[u]!=inf)continue;
vis[u]=d;
int x=u%2;
int y=(u+1)/2;
if(x==0&&horse[y])
{
pq.push({d,y*2-1});
}
for(auto t:a[y])
{
pq.push({d+t.second/(1+x),2*t.first-x});
}
}
vector<ll>d(n+1,inf);
for(int i=1;i<n+1;i++)
{
d[i]=min(vis[i*2],vis[2*i-1]);
}
return d;
};
auto dl=dijstra(1);
auto dr=dijstra(n);
ll ans=inf;
for(int i=0;i<n;i++)
{
ans=min(ans,max(dl[i+1],dr[i+1]));
}
if(ans!=inf)
cout<<ans<<endl;
else
cout<<-1<<endl;
}
int main(void )
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int test=1;
cin>>test;
while(test--)
{
solve();
}
return 0;
}
感悟:
-
以後卡題了可以考慮多開陣列記錄資料,演算法競賽不用省空間
-
優先佇列的dijstra演算法要實現的話只需要一個優先佇列,一個陣列,優先佇列用pair容器,pair用於儲存該點的編號和該點到起點的距離,陣列起始都是inf,用於記錄對應編號的點到起點的最短距離。首先將起點和距離0裝入佇列。然後迴圈以下操作:將隊首出隊,隊首是佇列中離起點最近的點,判斷該點是否已經賦值過,若是沒有被賦值過,則將該距離賦給該點,否則就不賦值跳過後面的操作,賦值後將該點相關聯的點和父點的離起點的距離加上父點到該點的距離一起存入佇列。迴圈以佇列為空時停止,此時陣列的數值就是對應點到起點的最短路徑了。
-
dijstra演算法的思想和dp非常的像,都是將區域性的最優解存起來,在後面的步驟可以直接用,這樣可以省去重複的計算
-
devc++有一個坑點,優先佇列中的greater<>中尖括號內需要加上變數型別,比如greater< int >,不然的話會報錯,有些編譯器不加也能執行