Slow Leak 题解

传送门:Slow Leak

前言:此题为牛客ICPC模拟赛上的 I 题,据说出自欧洲某地区的选拔赛。比赛的时候因为时间不够了就没写出来。就算有时间也不会 看了题解之后觉得很妙,看不懂题解,认为题解在教我仙术所以就单独把此题拿出来聊聊。

题目如下:
在这里插入图片描述
睡前聊一聊:
初看此题,我一度认为这就是一道求单源最短路径的题,直接上SPFA+记忆步数+特判就可以了,因为那时距离比赛结束只有25min了,所以就只能死马当活马医,A了血赚,WA了不亏。于是队友立刻用我口胡的算法写了一发SPFA,蒟蒻永远充满幻想,于是总是受到社会的毒打 不出意外, WA飞了。
可是本蒟蒻不甘心啊!我随口说的思路怎么可能错!于是赛后打开题解,看如痴如醉(不明所以 )就跟着题解的方法写了一遍,就一发入魂,AC了。
虽然过了,但题解中说的方法让我感到不解,为什么可以这样写呢?


思路:
如果把问题化简一下:我 开小汽车天涯海北 ,(其实天涯和海北在同一个地方,但这里就理解成很远很远的两个地方就可以了 ),求天涯到海北的最短路径。

但是 小汽车 的油箱是有限的,所以必须在油耗尽之间开到加油站。于是这个问题不能简单的用最短路去求,而要考虑加油站的问题。

建图不用多说,只要写法不阴间就可以了。

建图之后要跑一次Floyd,找到两点之间的最短路径,如果这两件之间的路径大于你的小汽车能跑的距离就直接赋值INF,表示这两点不可能不过加油站直线到。这样做可以简化图形。

我们再进一步考虑,天涯和海北两个地方相距那么远,我们开小汽车要经过多少个加油站啊!那么把需要经过的加油站缩小成一个点,这些点链接起来是不是就成了一条线了呢?

所以直接引出来:找最短路实际上就是找所有加油站的最小路。换句话说,就是建一个以加油站为点的图,找到一条最短路就可以了!

这时候有人会说:这不是胡搞吗?为什么一定要从加油站到加油站?老子开特斯拉,跑你个千百公里不是事情!

确实!你的特斯拉牛逼!但是天涯和海北相距十万八千里,你开特斯拉也不一定能直接跑到 ,(开个玩笑)所以,问题可以进一步转换:因为你不能一次性到达海北,所以你在途中必须经过至少一个加油站,因为在之前跑了一次Floyd,所以已经知道两个加油站之间的最短路,所以其他城市是不必要的了,(其他城市存在的目的就是找到两个加油站之间的最短路径)。即直接提取加油站建过一张图。

建完图之后这道题就是一个裸体了,直接迪杰特斯拉(斯特拉)或者Floyd跑一遍最短路就可了。


AC代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll INF=0x3f3f3f3f3f;
const int MAXN=505;
int flag[MAXN];
ll s[MAXN][MAXN];
ll g[MAXN];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n,m,t,d;
	cin>>n>>m>>t>>d;
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(i==j)s[i][j]=0;else s[i][j]=INF;
	for(int i=2;i<=t+1;i++)
	{
		cin>>g[i]; 
	}
	g[1]=1;g[t+2]=n;
	while(m--)
	{
		int u,v,w;
		cin>>u>>v>>w;
		s[v][u]=s[u][v]=w;
	} 
	for(int k=1;k<=n;k++)
	{
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(s[i][j]>s[i][k]+s[k][j])
				{
					s[i][j]=s[i][k]+s[k][j];
				}
			}
		}
	}
	for(int i=1;i<=t+2;i++)
	{
		for(int j=1;j<=t+2;j++)
		{
			int u=g[i],v=g[j];
			if(s[u][v]>d)s[u][v]=INF;
		}
	}
	for(int k=1;k<=t+2;k++)
	{
		for(int i=1;i<=t+2;i++)
		{
			for(int j=1;j<=t+2;j++)
			{
				int u=g[i],v=g[j],kk=g[k];
				if(s[u][v]>s[u][kk]+s[kk][v])s[u][v]=s[u][kk]+s[kk][v];
			}
		}
	}
	if(s[1][n]==INF)cout<<"stuck"<<endl;
	else cout<<s[1][n]<<endl;
	return 0;
 } 

后记:
机缘巧合之下听到学长把迪杰斯特拉说出迪杰特斯拉,觉得莫名喜感,于是就选特斯拉作为例子了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值