E. 激活锚点

知识点:最小生成树

个传送锚点的坐标建无向完全图,边权为两点间的距离。

求出最小生成树的权值。

再加上距离起点最近的传送锚点的距离,即为答案。

最小生成树用 Prim 算法(时间复杂度:)或 Kruskal 算法(时间复杂度:)都能过。

常见错误解法:把起点坐标和传送锚点的坐标放到一起跑最小生成树。

似乎还有高级做法,显然超纲了,不讲了。

标程

C++

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

constexpr double INF = 1e18;

struct Point
{
    int x, y;
};

double dist(const Point &p1, const Point &p2)
{
    return hypot(p1.x - p2.x, p1.y - p2.y);
}

// Prim 算法求最小生成树
double prim(const vector<Point> &points)
{
    int n = points.size();
    vector<bool> visited(n);
    vector<double> min_dist(n, INF);
    min_dist[0] = 0;

    double res = 0;
    for (int i = 0; i < n; ++i)
    {
        int u = -1;
        for (int j = 0; j < n; ++j)
        {
            if (!visited[j] && (u == -1 || min_dist[j] < min_dist[u]))
            {
                u = j;
            }
        }

        visited[u] = true;
        res += min_dist[u];
        for (int v = 0; v < n; ++v)
        {
            if (!visited[v])
            {
                min_dist[v] = min(min_dist[v], dist(points[u], points[v]));
            }
        }
    }

    return res;
}

void solve()
{
    Point p0;
    cin >> p0.x >> p0.y;

    int n;
    cin >> n;
    vector<Point> points(n);
    for (auto &[x, y] : points)
    {
        cin >> x >> y;
    }

    double ans1 = prim(points); // 最小生成树
    double ans2 = INF; // 距离起点最近的点
    for (const auto &p : points)
    {
        ans2 = min(ans2, dist(p0, p));
    }
    double ans = ans1 + ans2;
    cout << ans << '\n';
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cout.setf(ios::fixed);
    cout.precision(15);

    int T;
    cin >> T;
    while (T--)
    {
        solve();
    }
}

Java

import java.util.*;
import java.io.*;

public class Main {
    static Kattio io = new Kattio();

    static double dist(int x1, int y1, int x2, int y2) {
        int dx = x1 - x2;
        int dy = y1 - y2;
        return Math.hypot(dx, dy);
    }

    static double prim(int n, int[] x, int[] y) {
        double[] minDist = new double[n];
        boolean[] visited = new boolean[n];
        Arrays.fill(minDist, Double.MAX_VALUE);
        minDist[0] = 0;
        double ans = 0;
        for (int i = 0; i < n; ++i) {
            int u = -1;
            for (int j = 0; j < n; ++j) {
                if (!visited[j] && (u == -1 || minDist[j] < minDist[u])) {
                    u = j;
                }
            }
            visited[u] = true;
            ans += minDist[u];
            for (int v = 0; v < n; ++v) {
                if (!visited[v]) {
                    double d = dist(x[u], y[u], x[v], y[v]);
                    minDist[v] = Math.min(minDist[v], d);
                }
            }
        }
        return ans;
    }

    static void solve() {
        int x0 = io.nextInt();
        int y0 = io.nextInt();
        int n = io.nextInt();
        int x[] = new int[n];
        int y[] = new int[n];
        for (int i = 0; i < n; ++i) {
            x[i] = io.nextInt();
            y[i] = io.nextInt();
        }
        double ans1 = prim(n, x, y);
        double ans2 = Double.MAX_VALUE;
        for (int i = 0; i < n; ++i) {
            ans2 = Math.min(ans2, dist(x0, y0, x[i], y[i]));
        }
        double ans = ans1 + ans2;
        io.printf("%.15f\n", ans);
    }

    public static void main(String[] args) {
        int T = io.nextInt();
        while (T-- > 0) {
            solve();
        }

        io.close();
    }
}

class Kattio extends PrintWriter {
    private BufferedReader r;
    private StringTokenizer st;
    // 标准 IO
    public Kattio() { this(System.in, System.out); }
    public Kattio(InputStream i, OutputStream o) {
        super(o);
        r = new BufferedReader(new InputStreamReader(i));
    }
    // 在没有其他输入时返回 null
    public String next() {
        try {
            while (st == null || !st.hasMoreTokens())
                st = new StringTokenizer(r.readLine());
            return st.nextToken();
        } catch (Exception e) {}
        return null;
    }
    public int nextInt() { return Integer.parseInt(next()); }
    public double nextDouble() { return Double.parseDouble(next()); }
    public long nextLong() { return Long.parseLong(next()); }
}

Python

import math


def prim(points):
    res = 0.0
    n = len(points)
    dist = [math.inf] * n
    visited = [False] * n
    dist[0] = 0
    for _ in range(n):
        u = -1
        for i in range(n):
            if not visited[i] and (u == -1 or dist[i] < dist[u]):
                u = i
        visited[u] = True
        res += dist[u]
        for v in range(n):
            if not visited[v]:
                dist[v] = min(dist[v], math.hypot(points[u][0] - points[v][0], points[u][1] - points[v][1]))
    return res


def solve():
    x0, y0 = map(int, input().split())
    n = int(input())
    points = []
    for _ in range(n):
        x, y = map(int, input().split())
        points.append((x, y))
    dist = prim(points)
    # 牛客网的 Python 版本还不支持 math.dist,所以只能用 math.hypot
    ans = dist + min(math.hypot(x0 - pt[0], y0 - pt[1]) for pt in points)
    print(f"{ans:.15f}")


T = int(input())
for _ in range(T):
    solve()