【题解】Star Way To Heaven()

题目大意

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

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

输入格式

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

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

输出格式

一行一个数表示答案。保留到小数点后9位。

一行一个数表示答案。保留到小数点后9位。

样例

样例输入
10 5 2
1 1
2 3

样例输出
1.118033989

样例输入
10 5 2
1 1
2 3

样例输入
10 5 2
1 1
2 3

样例输出
1.118033989

样例输出
1.118033989

Solution

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

怎么利用最小生成树呢?

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

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

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

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

Code

#include <cstdio>
#include <cmath>
#include <iostream>
using namespace std;
const int MAXN = 6005, INF = 1e9;
int n, m, k;
double x[MAXN], y[MAXN], dis[MAXN], ans;
bool vis[MAXN];
double Dis(int i, int j) {
	return sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]));
}
int main() {
    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];
    }
    k++;
    dis[k] = m;
    while (1) {
        int p = -1;
        for (int i = 1; i <= k; i++)
            if (!vis[i] && (p == -1 || dis[i] < dis[p]))
                p = i;
        if (p == -1)
        	continue;
        vis[p] = 1;
        ans = max(ans, dis[p]);
        if (p == k)
            break;
        for (int i = 1; i < k; i++)
            dis[i] = min(dis[i], Dis(i, p));
        dis[k] = min(dis[k], y[p]);
    }
    printf("%.9lf", ans * 0.5);
    return 0;
}
————————

题目大意

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

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

输入格式

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

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

输出格式

一行一个数表示答案。保留到小数点后9位。

一行一个数表示答案。保留到小数点后9位。

样例

样例输入
10 5 2
1 1
2 3

样例输出
1.118033989

样例输入
10 5 2
1 1
2 3

样例输入
10 5 2
1 1
2 3

样例输出
1.118033989

样例输出
1.118033989

Solution

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

怎么利用最小生成树呢?

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

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

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

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

Code

#include <cstdio>
#include <cmath>
#include <iostream>
using namespace std;
const int MAXN = 6005, INF = 1e9;
int n, m, k;
double x[MAXN], y[MAXN], dis[MAXN], ans;
bool vis[MAXN];
double Dis(int i, int j) {
	return sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]));
}
int main() {
    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];
    }
    k++;
    dis[k] = m;
    while (1) {
        int p = -1;
        for (int i = 1; i <= k; i++)
            if (!vis[i] && (p == -1 || dis[i] < dis[p]))
                p = i;
        if (p == -1)
        	continue;
        vis[p] = 1;
        ans = max(ans, dis[p]);
        if (p == k)
            break;
        for (int i = 1; i < k; i++)
            dis[i] = min(dis[i], Dis(i, p));
        dis[k] = min(dis[k], y[p]);
    }
    printf("%.9lf", ans * 0.5);
    return 0;
}