做前分析
本题明显的一个多源/多汇最短路问题,理应用floyd算法 ,但是N<,而floyd时间复杂度为O(),所以floyd不能拿到全部的分。
而多源/多汇最短路问题的另一个常见做法:单源最短路+超级源点/汇点(差不多意思,一个是起点,一个是终点,用方向体现),例如本题,权值全是正的且n~m(为稀疏图),考虑dijkstra算法堆优化版,时间复杂度为约等于5千万,成立。
代码注意点
本题是稀疏图,用邻接表存储,要把表头数组h[N]全部赋-1;
dijkstra步骤:
1.初始化距离,起点赋予0,其余为无穷大(本题起点为超级源点0,而非1)
2.循环n次,找n个点到起点0的最短距离
- 找当前距离起点距离最短的点,认为此时该点距离起点距离最近
- 标记该点已经是找到最短距离的点
- 用该点更新取余与之相连的点距离
堆优化注意点:
- 用stl里优先队列可以实现堆.
- 优先队列中无法保证元素个数,我们把所以距离变化的点全部存入,一个点大概率会被存入多次,所以用判重数组st记录,已经找到最短距离点不在进行操作
超级源点/超级汇点:
- 同时有多个源点与汇点-----同时建立超级源点与超级汇点
- 有多个源点与一个汇点------建立超级源点,则汇点到多个源点中的最短距离为到超级源点的最短距离
- 有多个汇点与一个源点-------建立超级汇点
- 超级源点:正常i=0不会用,认为0为超级源点,add(0,源点,0);(只要设0-源点,说明该点为源点);s.push({0,0}); dis[0]=0
- 超级汇点:正常i=0不会用,认为0为超级汇点,add(源点,0,0);(只要设源点-0,说明该点位超级汇点)
本题的数据陷阱:本题为无向图,则idx会变为2*M,而我们又设立了超级源点,k<N,最大为N,则我们应该设N=300010才够
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N=300010;
typedef pair<int,int> pll;
#define x first
#define y second
int h[N],w[N],ne[N],e[N],idx;
int dis[N],st[N];
int n,m,k,q;
void add(int a,int b,int c)
{
e[idx]=b; w[idx]=c; ne[idx]=h[a]; h[a]=idx++;
}
void dijkstra()
{
memset(dis,0x3f,sizeof dis);
dis[0]=0;
priority_queue<pll,vector<pll>,greater<pll>>s;
s.push({0,0});
while(s.size())
{
pll t=s.top(); s.pop();
int distance=t.x,ver=t.y;
if(st[ver])continue;
st[ver]=1;
for(int i=h[ver];i!=-1;i=ne[i])
{
int j=e[i];
if(dis[j]>distance+w[i])
{
dis[j]=distance+w[i];
s.push({dis[j],j});
}
}
}
}
int main()
{
memset(h,-1,sizeof h);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int a,b,c; scanf("%d%d%d",&a,&b,&c);
add(a,b,c); add(b,a,c);
}
cin>>k;
while(k--)
{
int a; scanf("%d",&a);
add(0,a,0);
}
dijkstra();
cin>>q;
while(q--)
{
int x; scanf("%d",&x);
cout<<dis[x]<<endl;
}
}