链式前向星建图+优先队列优化的dijkstra 算法求多个终点的最短路算法

Time Limit

 

1000MS

Memory Limit

 

256MB

                                                  “是时候表演真正的技术了”

Description

 

众所周知,伊泽瑞尔是一位自信十足的探险家,他寻求冒险的足迹遍布符文之地。

这一天,他来到了一个古老的金字塔前面,他通过观察发现金字塔四周的墙壁上总共有nnn个入口。

他使用最先进的探测设备探测到这个金字塔内部有nnn个密室,为了方便区分,伊泽瑞尔将这nnn个密室按1,2,3,...,n1,2,3,...,n1,2,3,...,n进行了标号。同时他探测到其中有kkk个密室是有宝物的,这nnn个密室由mmm条通道连接起来,且任意两个密室都至少有一条路径可以互达,而墙外的nnn个入口可以进入这nnn个对应的密室(比如从第iii个入口进入就到达了编号为iii的密室)。

现在伊泽瑞尔想知道,如果他从第xxx个入口进入金字塔,那么他走到离他最近有宝物的密室需要多长时间。(如果xxx这个密室里已经有宝物的话,就不用继续去别的密室了)

“没有时间可以浪费了!”

Input

 

第一行输入三个整数n,m,kn,m,kn,m,k,代表有nnn个密室,mmm条通道,有kkk个有宝物的密室

第二行输入kkk个整数,代表那kkk个有宝物的密室

接下来m行,每行三个整数u,v,wu,v,wu,v,w,代表密室uuu和密室vvv之间有一条通道,走过这条通道所需要的时间是www。

接下来一行输入一个整数qqq,代表有qqq次询问。

接下来qqq行,每行输入一个整数xxx。

数据范围:

1<=n<=105,1<=m<=3∗105,1<=k<=2000;1<=n<=10^5,\;1<=m<=3*10^5,\;1<=k<=2000;1<=n<=105,1<=m<=3∗105,1<=k<=2000;

1<=u,v<=n,0<=w<=109;1<=u,v<=n,\;0<=w<=10^9;1<=u,v<=n,0<=w<=109;

1<=q<=105,1<=x<=n.1<=q<=10^5,\;1<=x<=n.1<=q<=105,1<=x<=n.

Output

对于每次询问,输出一个答案。

Sample

Input

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

output

3 6 0 4 5

题目分析

          这个题目为本校的校赛题目,链接为http://10.64.70.166/problem/1097   ,如果做不了,就请自行对拍吧,我只是把这个当作一个模板来保存的。

Dijkstra算法求单源最短路,这个东西很多博客里面都有详细的介绍的,这里我就说一下我的代码里面的实现。

首先我使用的是链式前向星建图法,我们用一个head[x]记录以x为起点的最后一条边(在这里最后一条输入的以x为起点的边就是这个最后一条边),而head[x]代表的是一个下标,结构体数组Edge的下标,代表一条边,Edge中的to代表这条边的指向,val代表权值,to指向以同一顶点为起点的其余边,实在不懂的话,请自行百度吧…..

然后我们介绍一下Dijkstra算法,这个算法的核心就是贪心算法(Greedy),我们先把起点和其到起点的距离0入队列{0,start},然后我们找到队列中到起点最近的点,最开始的时候我们的点为start,也就是起点,由于start是当前离起点最近的点,也就是说这个点已经是离起点最近的点了(要是用其余任何顶点对其缩短,因为没有点比当前点离根结点更近,显然没有可能用任何点来最短当前点到起点的距离),那么我们用当前的这个点,对所有和当前点相连接的点进行缩短距离,因为当前的这个点已经离起点最近,那么用这个点来缩短距离就比较可靠了。

整个过程建立在用已经找到最短距离点来缩短其余点到起点的距离,这就是核心思想


一起说一下Floyed吧,和这个题无关,我自己理解吧

Floyed算法求多元最短路,这里的核心思想是动态规划,这个地方的代码很简单,核心代码也就4行:

for (int k = 1;k <= n; k++)

    for (int i = 1;i <= n; i++)

       for (int j = 1;j <= n; j++)

           dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);

在这里我们枚举所有顶点当作中间点,利用顶点k来缩短任意两点 I , j 之间的距离,说到应就差不多了吧,最短路算法大多在考虑如何利用起点的点缩短其余各点之间的距离

 

代码区

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
typedef long long ll;
using namespace std;
const int Max = 3e5 + 10;
const int inf = 0x3f3f3f3f;

typedef struct Edge{
	int next;
	int to;
	ll v;
	bool operator<(const Edge& e) const
	{
		return v < e.v;
	}
}Edge;

Edge edge[Max<<2];
int n, m, k,cnt;
int start[3000];
int head[Max];	//head[i]表示以i为起点的
ll dis[Max];	//记录各个点到有宝藏的房间的距离


void add(int s,int e,ll v)
{
	edge[cnt].to = e;
	edge[cnt].v = v;
	edge[cnt].next = head[s];
	head[s] = cnt++;
}

void init()
{
	for (int i = 1; i <= k; i++)
	{
		scanf("%d", start + i);
	}
	memset(head, -1, sizeof(head));
	memset(dis, inf, sizeof(dis));
	for(int i = 1 ; i <= m ; i++)
	{
		int s, e;
		ll v;
		scanf("%d%d%lld", &s, &e, &v);
		add(s, e, v);
		add(e, s, v);
	}
}

void dijkstra(int now)
{
	priority_queue<pair<ll,ll>,vector<pair<ll, ll>>,greater<pair<ll, ll>> >q;
	bool vis[Max];	//记录是否访问
	memset(vis, false, sizeof(vis));
	//vis[now] = true;
	dis[now] = 0;
	q.push({ dis[now],now });//
	while(!q.empty())
	{
		int s = q.top().second;//找到离上一点最近的
		q.pop();
		if (vis[s]) continue;
		vis[s] = true;
		for (int i = head[s]; i != -1; i = edge[i].next) {//查找所有以s为起点的点的边,看是否可以进行优化
			int u = edge[i].to;
			if (!vis[u] && dis[u] > dis[s] + edge[i].v) {
				dis[u] = dis[s] + edge[i].v;
				q.push({ dis[u],u });
			}
		}
	}
}

int main()
{
	while (scanf("%d%d%d", &n, &m, &k) != EOF)
	{
		init();
		for(int i = 1;  i <= k ;i++)	//多个目标点的情况
		{
			dijkstra(start[i]);
		}
		int t;
		scanf("%d", &t);
		while(t--)
		{
			int x;
			scanf("%d", &x);
			printf("%lld\n", dis[x]);
		}
	}
	return 0;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值