本题可抽象为一个汇点到多个源点的最短路问题
新增一个点(超级源点),超级源点连向所有商店一条边,权值为0,再从超级源点做单源最短路。
即所有的商店作为起点,查询的村庄,做为终点。
新增一点作为超级源点
超级源点连向所有商店,权值为0
从超级源点开始做单源最短路
总结:
超级源点跟超级汇点是模拟出来的虚拟点,多用于图中:
- 同时有多个源点和多个汇点,建立超级源点和超级汇点
- 同时有多个源点和一个汇点,建立超级源点
- 同时有多个汇点和一个源点,建立超级汇点
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1e5 + 10, M = N*3;//无向图2N,但是有个超级源点再向每个点连接一条边,所以是3N;
int h[N], e[M], ne[M], idx, w[M];
int a[N];
int n, m;
int dist[N];
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
typedef pair<int, int> PII;
void dj()
{
memset(dist, 0x3f, sizeof (dist));
dist[0] = 0; //起点是0,不是1,因为加了虚拟源点!
//维护的是节点的距离,但是必须还要知道节点编号才好操作.
//小根堆:加两个参数!
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, 0}); //起点入队!起点到起点的距离为0!
while (heap.size()) //当堆不为空的时候
{
auto t = heap.top();
heap.pop();
int v = t.second;
if (st[v]) continue;
st[v] = true; //表示这个点已经确定了最短路!
for (int i=h[v]; ~i; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[v] + w[i])
{
dist[j] = dist[v] + w[i];
heap.push({dist[j], j});
}
}
}
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof h);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c); //选择添加边!
add(b, a, c);
}
int q;
scanf("%d", &q);
while (q --) //以商店做为起点!
{
int x;
scanf("%d", &x);
add (0, x, 0); //建立一个虚拟头结点,让虚拟源点指向所有的起点!
}
dj(); //从虚拟源点出发:做一遍djkstra算法!
//再输入查询:查询的节点都是终点!以虚拟源点做起点,跑一遍dj,然后找出哪个起点(商店)到某一点最近
int k;
scanf("%d", &k);
while (k --)
{
int x;
scanf("%d", &x);
cout << dist[x] << endl;
}
return 0;
}