AcWing 1488. 最短距离

AcWing 1488. 最短距离_第1张图片

先看题目。其实就是求的是每个点到最近的商店的距离,也就是任意两点间的最短路。

我们最熟悉的最短路的求法就是迪杰斯特拉算法,但是dijkstra求的是单源最短路,寻找一个有向图中从任意节点到其他节点的最短路径。

但是我们观察一下本题,有好多次查询,而且查询的是该点到最近的商店的距离,想想好像是把该点到所有商店的距离都求出来,然后找出最小的。

那么有点难过了,这不就是多个源点吗,多个源点用dijkstra感觉得重复多次地计算最短路啊。那这时间复杂度不得爆炸啊。

所以题解中提出了一种思路:既然是多个源点,那么我们就虚拟出一个超级源点,这个源点到每个商店的边长都是0。于是题目就转变为了:求任意点到超级源点地最短路径,现在我们只考虑超级源点这一个源点,那么就可以使用dijkstra算法了!

代码如下:

注意这里面N和M的值,虽然题目的数据范围是1e5,但是图中是无向边,所以要开成2倍,然后我们还要记录一下从超级源点到各个点的边,那么又要再开一倍,所以最后开了3*1e5.

#include
#include
#include
#include
//#define ll long long
using namespace std;
typedef pair PII;
const int N = 300010, M = 300010;
int n, m, k;
//h是邻接表的表头,e是存储每条边对应的节点,ne是当前节点的下一个节点,idx是节点的索引 
int h[N];
int e[M];
int idx;
int ne[M];
int w[M];
int dis[N];
bool vis[N];
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, greater>q;
	q.push({0, 0});
  
	while(q.size()){
		auto [d, x] = q.top();
		q.pop();
        if(vis[x]) continue;
        vis[x] = true;
		for(int i = h[x]; i != -1; i = ne[i]){
			int j = e[i];
			if(dis[j] > w[i] + d){
				dis[j] = w[i] + d;
				q.push({dis[j], j});
			}
		}
	}
}
int main(){
	scanf("%d%d", &n, &m);
	memset(h, -1, sizeof(h));
	for(int i = 0; i < m; i++){
	    int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		add(a, b, c);
		add(b, a, c);
	} 
	int k;
	scanf("%d", &k);
	for(int i = 0; i < k; i++){
		int x;
		scanf("%d", &x);
		add(0, x, 0);
	}
	dijkstra();
	int q;
	scanf("%d", &q);
	for(int i = 0; i < q; i++){
		int y;
		scanf("%d", &y);
		printf("%d\n", dis[y]);
	}
	
	return 0;
} 

本来很开心的,结果被无力改变的事情弄得心情很糟。

有点绝望了。

你可能感兴趣的:(蓝桥杯,算法)