zoukankan      html  css  js  c++  java
  • 最小距离「多源最短路」

    最小距离「多源最短路」

    题目描述

    给定一张 (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;
    }
    
  • 相关阅读:
    消息循环中的TranslateMessage函数和DispatchMessage函数,特别注意WM_TIMER消息
    单线程程序处理消息的方式!
    PeekMessage&GetMessage
    GetTickCount() 函数的作用和用法
    LPVOID 没有类型的指针
    sprintf详解
    memset用法详解
    C语言中%d,%o,%f,%e,%x的意义
    VS2013 C++ 动态链接库的生成
    visual studio 2013的C++开发环境不错--vs2013安装试用手记
  • 原文地址:https://www.cnblogs.com/hhhhalo/p/13535991.html
Copyright © 2011-2022 走看看