[CSP-S模拟测试]:迷宫(最短路)

题目传送门(内部题123)


输入格式

  输入文件的第一行为四个正整数$n,m,k,d$。
  接下来$m$行,每行三个整数$u,v,w$,描述一条无向道路。
  输入文件最后一行包含$k$个整数,为$p_0,p_1,...,p_{k-1}$。


输出格式

  一行一个整数,表示最坏情况下走出迷宫的最短路径的长度。如果最坏情况下走不出迷宫,输出$-1$。


样例

样例输入:

3 4 1 1
0 1 1
0 1 2
1 2 1
1 2 2
2

样例输出:

4


数据范围与提示

  对于$50\%$的数据,满足$m=n-1$。
  对于$90\%$的数据,满足$n\leqslant 1,000,m\leqslant 100,000$。
  对于$100\%$的数据,满足$3\leqslant n\leqslant 100,000,2\leqslant m\leqslant 1,000,000$,道路长度均为不超过$10^9$的正整数。


题解

对于$m=n-1$那$50$分,就是逗你开心的,你以为是棵树,然而却又可能有重边。

那就直接想正解吧。

转化一下题意,就是跑一个拿第$d+1$大的$dis[v]$转移$u$的最短路。

对于每一个点开一个堆就好了。

再讲个故事(你们都喜欢听我讲故事为什么?)

  上午困的痛不欲生……

  (痛不欲生可海星?)

  刚考完试的我:“你们谁能$hack$一下我的算法,!@#$%^&*,大根堆,!@#$%^&*”。

  台下一片沉默……

  下午刚来到机房准备改题的我:唉,我大根堆呢?!

  

故事讲完啦~

时间复杂度:$\Theta(m\log n)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
struct rec{int nxt,to,w;}e[2000001];
int head[100001],cnt;
int n,m,k,d;
int a[100001];
bool vis[100001],is[100001];
long long dis[100001],ans=0x3f3f3f3f3f3f3f3f;
priority_queue<long long>dq[100001];
priority_queue<pair<long long,int>,vector<pair<long long,int>>,greater<pair<long long,int>>>q;
void add(int x,int y,int w)
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	e[cnt].w=w;
	head[x]=cnt;
}
void Dij()
{
	memset(dis,0x3f,sizeof(dis));
	for(int i=1;i<=k;i++)dis[a[i]]=0;
	while(q.size())
	{
		int x=q.top().second;q.pop();
		if(vis[x])continue;vis[x]=1;
		for(int i=head[x];i;i=e[i].nxt)
		{
			dq[e[i].to].push(dis[x]+e[i].w);
			if(dq[e[i].to].size()>d)
			{
				if(dis[e[i].to]>dq[e[i].to].top())
				{
					dis[e[i].to]=dq[e[i].to].top();
					q.push(make_pair(dis[e[i].to],e[i].to));
				}
				dq[e[i].to].pop();
			}
		}
	}
}
int main()
{
	scanf("%d%d%d%d",&n,&m,&k,&d);
	for(int i=1;i<=m;i++)
	{
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		u++;v++;add(u,v,w);add(v,u,w);
	}
	for(int i=1;i<=k;i++){scanf("%d",&a[i]);a[i]++;q.push(make_pair(0,a[i]));}
	Dij();
	if(dis[1]==0x3f3f3f3f3f3f3f3f)puts("-1");
	else printf("%lld",dis[1]);
	return 0;
}

rp++

posted @ 2019-11-05 16:38  HEOI-动动  阅读(186)  评论(0编辑  收藏  举报