传送门: OJ 新生赛 2018华山论剑。
题目大意:从点1出发,按次序经过K个点,再走到点n。求最短路径。
解题思路:弗洛伊德算法可能超时,所以用单源最短路径算法,依次求出从1到a1,a1到a2.......ak到n的最短路径长度。为降低复杂度,必须使用邻接表存储结构,同时用优先队列进行优化。
#include <iostream>
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
struct node
{
int id,v;
bool operator<(const node B)const
{
return v>B.v;
}
};
int t,n,m,a[1005],x,y,z,k,d[1005],v[1005];
vector<pair<int,int> >e[1005];
void dij(int cur)
{
memset(d,127/3,sizeof d);
memset(v,0,sizeof v);
priority_queue<node>pq;
d[cur]=0;
int i,j;
for(i=1;i<=n;i++)
pq.push({i,d[i]});
for(i=1;i<=n;i++)
{
while(v[pq.top().id]) /**< 队列会进入id相同但v不同的重复元素,所以队头可能是之前已经选走的id */
pq.pop(); /**< 已选过的结点不能再选,删除 */
int t=pq.top().id;
pq.pop();
v[t]=1;
for(j=0;j<e[t].size();j++)
{
int y=e[t][j].first,z=e[t][j].second;
if(d[y]>d[t]+z)
{
d[y]=d[t]+z;
pq.push({y,d[y]});
}
}
}
}
int main()
{
int i,j;
cin>>t;
while(t--)
{
cin>>n>>m;
for(i=1;i<=n;i++)
e[i].clear();
memset(v,0,sizeof v);
for(i=1;i<=m;i++)
{
cin>>x>>y;/**< 实际上本题目实际无需存储边长,边长均为1,为方便读者,此代码采用标准写法存边 */
e[x].push_back({y,1});
e[y].push_back({x,1});
}
dij(1);/**< 计算1到其他点最短路径 */
cin>>k;
long long ans=0, pre=1;
for(i=1;i<=k;i++){
cin>>a[i];
ans+=d[a[i]];/**< pre到a[i]的最短路径 */
dij(a[i]);
pre=a[i];
}
cout<<ans+d[n]<<endl;
}
return 0;
}