Построение минимального дерева Штейнера в евклидовой плоскости
Условие:
Даны три точки в евклидовой плоскости с координатами A(x1,y1), B(x2,y2) и C(x3,y3). Необходимо построить минимальное дерево, соединяющее эти точки с минимальной общей длиной рёбер.
Разрешается добавление одной дополнительной точки S(xs,ys)(точки Штейнера), которая может уменьшить суммарную длину соединяющих отрезков.
Требования:
- Найти координаты точки S, которая минимизирует сумму расстояний от неё до заданных точек.
- Визуализировать полученное дерево с помощью библиотеки SFML.
- Вывести терминальные точки (красным), точку Штейнера (синим) и соединяющие рёбра (жёлтым).
Формат входных данных:
На вход подаются три пары вещественных чисел (x1,y1), (x2,y2), (x3,y3) — координаты заданных точек.
Формат выходных данных:
- Вычислить координаты точки Штейнера S(xs,ys).
- Вывести общую длину построенного дерева.
- Открыть графическое окно с визуализацией результата.
Пример работы программы:
Порядок решения задачи о минимальном дереве Штейнера
Шаг 1: Определение входных данных
Заданы три точки A(x1,y1), B(x2,y2) и C(x3,y3) в евклидовой плоскости. Нужно найти минимальное дерево, соединяющее их, с возможностью добавления одной точки Штейнера S(xs,ys), которая минимизирует сумму расстояний.
Шаг 2: Поиск точки Штейнера
Метод градиентного спуска применяется для нахождения оптимальной точки S.
2. Минимизация суммы расстояний
- Двигаемся в направлениях вверх, вниз, влево и вправо с некоторым шагом Δ, уменьшая его, когда не находим лучшего положения.
- Останавливаемся, когда шаг становится меньше заданной точности.
Шаг 3: Визуализация графа (SFML)
После нахождения S(xs,ys), строим дерево:
Шаг 4: Вывод результатов
- Красные точки — исходные точки
- Синяя точка — точка Штейнера
- Жёлтые линии — минимальное дерево
Код программы
#include <SFML/Graphics.hpp>
#include <iostream>
#include <cmath>
#include <vector>
#include <limits>
using namespace std;
using namespace sf;
struct Point {
    double x, y;
};
// Вычисление расстояния между двумя точками
double distance(Point a, Point b) {
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
// Сумма расстояний от точки p до всех остальных
double totalDistance(Point p, const vector<Point>& terminals) {
    double sum = 0;
    for (const auto& t : terminals) {
        sum += distance(p, t);
    }
    return sum;
}
// Метод градиентного спуска для поиска точки Штейнера
Point steinerPoint(const vector<Point>& terminals) {
    double step = 0.1;
    double precision = 1e-6;
    Point steiner = { 0, 0 };
    for (const auto& t : terminals) {
        steiner.x += t.x;
        steiner.y += t.y;
    }
    steiner.x /= terminals.size();
    steiner.y /= terminals.size();
    double prevDist = totalDistance(steiner, terminals);
    while (step > precision) {
        bool improved = false;
        for (double dx : {-step, step}) {
            for (double dy : {-step, step}) {
                Point newPoint = { steiner.x + dx, steiner.y + dy };
                double newDist = totalDistance(newPoint, terminals);
                if (newDist < prevDist) {
                    steiner = newPoint;
                    prevDist = newDist;
                    improved = true;
                }
            }
        }
        if (!improved) {
            step /= 2;
        }
    }
    return steiner;
}
// Основная функция
int main() {
    // Размер окна
    const int WIDTH = 600, HEIGHT = 600;
    // Исходные точки
    vector<Point> terminals = { {100, 500}, {500, 500}, {300, 100}};
    // Найдём точку Штейнера
    Point s = steinerPoint(terminals);
    // Создание окна SFML
    RenderWindow window(VideoMode(WIDTH, HEIGHT), "Steiner Tree Visualization");
    while (window.isOpen()) {
        Event event;
        while (window.pollEvent(event)) {
            if (event.type == Event::Closed)
                window.close();
        }
        // Очистка экрана
        window.clear(Color::Black);
        // Рисуем рёбра (жёлтые линии)
        for (const auto& t : terminals) {
            Vertex line[] = {
                Vertex(Vector2f(s.x, s.y), Color::Yellow),
                Vertex(Vector2f(t.x, t.y), Color::Yellow)
            };
            window.draw(line, 2, Lines);
        }
        // Рисуем терминальные точки (красные)
        for (const auto& t : terminals) {
            CircleShape terminal(5);
            terminal.setPosition(t.x - 5, t.y - 5);
            terminal.setFillColor(Color::Red);
            window.draw(terminal);
        }
        // Рисуем точку Штейнера (синяя)
        CircleShape steiner(5);
        steiner.setPosition(s.x - 5, s.y - 5);
        steiner.setFillColor(Color::Blue);
        window.draw(steiner);
        // Отображение кадра
        window.display();
    }
    return 0;
}Метод использует градиентный спуск для поиска точки Штейнера и SFML для визуализации. Это позволяет эффективно решать задачу и демонстрировать результат в графическом окне.