最小距离「多源最短路」

最小距离「多源最短路」

题目描述

给定一张 \(n\) 个点 \(m\) 条边的带边权连通无向图,其中有 \(p\) 个点是特殊点。

对于每个特殊点,求出它到离它最近的其它特殊点的距离。

输入格式

第一行三个整数 \(n,m,p\),第二行 \(p\) 个整数 \(x_1~x_p\) 表示特殊点的编号。接下来 \(m\) 行每行三个整数 \(u,v,w\) 表示一条连接 \(u\)\(v\),长度为 \(w\) 的边。

输出格式

输出一行 \(p\) 个整数,第 \(i\) 个整数表示 \(x_i\) 的答案。

样例

样例输入

5 6 3
2 4 5
1 2 4
1 3 1
1 4 1
1 5 4
2 3 1
3 4 3

样例输出

3 3 5

数据范围与提示

对于 \(10\%\) 的数据,\(n,m<=50000,p<=10\)

对于 \(40\%\) 的数据,\(n,m<=50000\)

对于另外 \(5\%\) 的数据,\(p=n\)

对于 \(100\%\) 的数据,\(1<=n,m<=2e5,2<=p<=n,1<=x_i<=n\)\(x_i\) 互不相同,\(1<=u,v<=n,1<=w<=1e9\)

思路分析

又是一个奇奇怪怪的最短路

  • 这题显然暴力的话是以所有特殊点为起点跑一遍最短路,然而只能过俩点
  • 所以我们考虑只跑一遍最短路,这时候就引入了一个概念——多源最短路(不是严格意义上的,因为无法求出任意两点的最短距离)
  • 广为人知的 \(Floyd\) 就是典型的多源最短路,那其他求最短路的算法就不能算了吗?
  • 然而事实是 \(Floyd\)\(O(n^3)\) 的效率使单源最短路算法被迫转型,其实只要稍微改动一部分就行了
    • \(Dijkstra\) 为例( \(Spfa\) 也可以,但做法有差异),将所有起点都压入堆里,距离置为 \(0\),这样我们就可以求出每个点到它最近的起点的距离
    • 跑完 \(Dijkstra\) 后,我们枚举每一条边,如果边两端的点最近的起点不同,即是由不同的起点拓展的,那么我们就可以对相应的起点进行更新

\(Code\)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define N 400010
#define R register
#define int long long
using namespace std;
inline int read(){
	int x = 0,f = 1;
	char ch = getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m,p,spl[N],head[N],f[N];
long long ans[N],dis[N];
bool vis[N],flag[N];
struct edge{
	int to,next,dis;
}e[N<<1];
int len = 1;
void addedge(int u,int v,int w){
	e[++len].to = v;
	e[len].dis = w;
	e[len].next = head[u];
	head[u] = len;
}
struct node{
	int num,dis;
	node(){}
	node(int _num,int _dis){num = _num,dis = _dis;}
	bool operator <(const node &a)const{
		return dis > a.dis;
	}
};
void Dij(){
	priority_queue<node>q;
	fill(dis+1,dis+1+n,1e18);
	for(int i = 1;i <= p;i++){
		dis[spl[i]] = 0,f[spl[i]] = spl[i]; //不同起点的起点是本身
		q.push(node(spl[i],0));
	}
	while(!q.empty()){
		node p = q.top();q.pop();
		int u = p.num;
		if(vis[u])continue;
		vis[u] = 1;
		for(R int i = head[u];i;i = e[i].next){
			int v = e[i].to;
			if(dis[v]>dis[u]+e[i].dis){
				dis[v] = dis[u]+e[i].dis;
				f[v] = f[u]; //记录起点
				q.push(node(v,dis[v]));
			}
		}
	}
}
signed main(){
	n = read(),m = read(),p = read();
	for(R int i = 1;i <= p;i++){
		int x;x = read();
		spl[i] = x,flag[x] = 1;
	}
	for(R int i = 1;i <= m;i++){
		int u,v,w;u = read(),v = read(),w = read();
		addedge(u,v,w),addedge(v,u,w);
	}
	Dij();
	fill(ans+1,ans+1+n,1e18);
	for(int i = 2;i <= len;i += 2){
		int x = e[i].to,y = e[i^1].to,w = e[i].dis; //反向边的终点即原边的起点,这样就可以确定每条边的端点了
		if(f[x]!=f[y]){
			ans[f[x]] = min(ans[f[x]],dis[x]+dis[y]+w);
			ans[f[y]] = min(ans[f[y]],dis[x]+dis[y]+w);
		}
	}
	for(int i = 1;i <= p;i++)printf("%lld ",ans[spl[i]]);
	return 0;
}
posted @ 2020-08-20 16:49  HH_Halo  阅读(412)  评论(0编辑  收藏  举报