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;
}