[CSP-S模拟测试]:Star Way To Heaven(最小生成树Prim)

题目描述

  小$w$伤心的走上了$Star way to heaven$。
  到天堂的道路是一个笛卡尔坐标系上一个$n imes m$的长方形通道(顶点在$(0,0)$和$(n,m)$),小$w$从最左边任意一点进入,从右边任意一点走到天堂。
  最左最右的距离为$n$,上下边界距离为$m$。
  其中长方形内有$k$个$Star$,每个$Star$都有一个整点坐标,$Star$的大小可以忽略不计。
  每个$Star$以及长方形上下两个边缘(宇宙的边界)都有引力,所以为了成功到达$heaven$小$w$离他们越远越好。
  请问小$w$走到终点的路径上,距离所有星星以及宇宙的边界的最小距离最大值可以为多少?


输入格式

一行三个整数$n,m,k$。
接下来$k$行,每行两个整数$x_i,y_i$表示一个点的坐标。


输出格式

一行一个整数表示答案,绝对误差不能超过${10}^{-6}$。


样例

样例输入:

10 5 2
1 1
2 3

样例输出:

1.11803399


数据范围与提示

对于$20\%$的数据,$kleqslant 10$。
对于$50\%$的数据,$kleqslant 400$。
对于$80\%$的数据,$kleqslant 1,000$。
对于$100\%$的数据,$kleqslant 6,000,n,mleqslant {10}^6$。


题解

先来重申一下题意(考试的时候没弄明白,当场爆炸),路径不一定直的,中途可以拐弯(我也不知道为什么我考试的时候会下意识以为路径一定是直的)。

明确完了,开始讲题。

首先,我们如果要从两个$Star$之间穿过,那么选择从它们的中点穿过一定不劣,所以我们的问题就转化为了找从左边到右边所有路径中所要穿过的$Star$中离的最近的两个的最大值(语文不好,见谅),这个东西类似于[NOIP模拟测试]:$water$(BFS)这道题,但是当时我是使用$BFS$直接$A$掉的,其实正解就是最小生成树,那么这道题我们也用最小生成树。

怎么利用最小生成树呢?

首先是建图,先将所有的$Star$相互连边,然后再将最靠上的一个点连向上边界,最靠下的一个点连向下边界。

然后我们考虑怎么利用最小生成树,我们要从左边到右边,也就要穿过在最小生成树中从上边界到下边界最短路径中的一条边,而最优策略一定是走最长的那一条边,所以整道题便转化为了找最小生成树中从上边界到下边界的最短路径中的最长边,答案极为这条边边长的一半。

但是我们这里需要注意的是空间问题,显然$Kruskal$算法在稠密图中的空间复杂度高的可怕,而这道题考的我们便是对$Prim$算法的利用,所以有的不常用的算法至少还是要知道其原理的。

下面代码比较清奇,我在$prim$算法上稍作了改动,代码复杂度大幅度减小。

时间复杂度:$Theta(n^2)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
double x[7000],y[7000];
double dis[7000];
bool vis[7000];
double ans;
int main()
{
	dis[0]=20020923;
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=k;i++)
	{
		scanf("%lf%lf",&x[i],&y[i]);
		dis[i]=m-y[i];
	}
	dis[k+1]=m;
	while(1)
	{
		int flag=0;
		for(int i=1;i<=k+1;i++)
			if(!vis[i]&&dis[i]<dis[flag])flag=i;
		vis[flag]=1;
		ans=max(ans,dis[flag]);
		if(flag==k+1)break;
		for(int i=1;i<=k;i++)
			dis[i]=min(dis[i],sqrt((x[i]-x[flag])*(x[i]-x[flag])+(y[i]-y[flag])*(y[i]-y[flag])));
		dis[k+1]=min(dis[k+1],y[flag]);
	}
	printf("%.8lf",ans/2);
	return 0;
}

rp++

原文地址:https://www.cnblogs.com/wzc521/p/11382837.html