Максимальная площадь, занятая треугольниками внутри правильного многоугольника

Задача звучит так:

Задается правильный n-угольник со стороной равной 1. Нужно найти максимальную площадь, занятую треугольниками, если вершины треугольников должны совпадать с вершинами многоугольника, но не пересекаться между собой (в том числе в вершинах).

Не особо понимаю, как к этой задаче подступиться


Ответы (4 шт):

Автор решения: MBo

Ну если не полной генерацией всего, что можно, то попробовать так:

Фиксируем первую вершину (ввиду симметрии), выбираем всеми способами (исключая симметричные уже имеющимся варианты) ещё две вершины первого треугольника, рекурсивно вызываем функцию для трёх (возможно пустых) оставшихся многоугольников, не включающих использованные вершины

AEJ взяли, решаем подзадачу для BCD (тут однозначно), FGHI, LK (вырожден):

введите сюда описание изображения

→ Ссылка
Автор решения: Harry

Интересная получилась задачка... Вот первые решения — от 3 до 20 и соответственно графики. Числа внутри картинок — площадь покрытия и ее отношение к площади многоугольника (на графике соответственно синяя и красная линия). Если кто какую математику сумеет привязать и рассказать, было бы интересно посмотреть...

введите сюда описание изображения

введите сюда описание изображения

Update 1

На 25-угольнике проклюнулся подтверждающий идею пятый треугольник:

введите сюда описание изображения

Переписал с применением ДП, скорости достаточно, чтоб просчитывать сотни... На картинках уже ничего не разглядеть, но при N=282 (пятый порядок треугольников) площадь треугольников 93.6% от площади многоугольника.

Update 2

Ну, и последняя капля :) Пусть

введите сюда описание изображения - площадь N-угольника, а введите сюда описание изображения - площадь покрытия треугольниками.

По моим прикидкам, при больших N (ну, там, тысячи хотя бы...)

введите сюда описание изображения

Update 3

По просьбам на e-mail и в комментариях. Вот две программки, писано на бегу, когда вечером свет есть, так что качество — какое уж есть. Все компилил VC++2019.

Получение оптимального решения для N-угольника.

#include <vector>
#include <iostream>
#include <iomanip>
#include <cmath>
#include <numbers>
#include <array>
#include <map>

using namespace std;

struct Pnt
{
    double x,y;
};

Pnt pnt(int i, int N)
{
    Pnt p;
    p.x = cos(2*numbers::pi*i/N);
    p.y = sin(2*numbers::pi*i/N);
    return p;
}

using Trigon = array<int,3>;
using Soltri = vector<Trigon>;

struct Solution
{
    int N, count;
    double S = -1;
    Soltri sol;
};

// pair<int,int> - N и количество вершин (отсчет c 0)
map<pair<int,int>,Solution> Rep;

// Площадь треугольника
double Area(int i, int j, int k, int N)
{
    return abs(sin(2*numbers::pi*(j-i)/N) + sin(2*numbers::pi*(k-j)/N) + sin(2*numbers::pi*(i-k)/N))/2;
}

// pair<int,int> - начальная вершина и их количество
double S(pair<int,int> d, Soltri& st, int N)
{
    if (d.second < 3) return 0;
    if (auto sl = Rep.find(make_pair(N,d.second)); sl != Rep.end())
    {
        for(auto t: sl->second.sol)
        {
            t[0] += d.first;
            t[1] += d.first;
            t[2] += d.first;
            st.push_back(t);
        }
        return sl->second.S;
    }

    double maxS = 0;
    Soltri trs;

    for(int i = d.first; i < d.first + d.second - 2; ++i)
        for(int j = i+1; j < d.first + d.second - 1; ++j)
            for(int k = j+1; k < d.first + d.second; ++k)
            {
                Soltri cur;
                Trigon t {i,j,k};
                cur.push_back(t);
                double pS = Area(i,j,k,N);
                pair<int,int> a[4];
                a[0] = make_pair(d.first,i - d.first);
                a[1] = make_pair(i+1,j - i - 1);
                a[2] = make_pair(j+1,k - j - 1);
                a[3] = make_pair(k+1,d.second - k - 1);

                pS += S(a[0],cur,N) + S(a[1],cur,N) + S(a[2],cur,N) + S(a[3],cur,N);

                if (pS > maxS)
                {
                    maxS = pS;
                    trs = cur;
                }
            }
    Solution sol;
    sol.N = N; sol.count = d.second; sol.S = maxS;
    for(auto m: trs)
    {
        st.push_back(m);
        for(int& tg: m) tg -= d.first;
        sol.sol.push_back(m);
    }
    Rep.insert(make_pair(make_pair(N,d.second),sol));

    return maxS;
}

double Sol(Soltri& st, int N)
{
    double maxS = 0;
    Soltri trs;
    int i = 0;
    for(int j = 1; j < N - 1; ++j)
        for(int k = j+1; k < N; ++k)
        {
            Soltri cur;
            Trigon t{i,j,k};
            cur.push_back(t);
            double pS = Area(i,j,k,N);
            pair<int,int> a[4];
            a[0] = make_pair(i+1,j - i - 1);
            a[1] = make_pair(j+1,k - j - 1);
            a[2] = make_pair(k+1,N - k - 1);
            pS += S(a[0],cur,N) + S(a[1],cur,N) + S(a[2],cur,N);
            if (pS > maxS)
            {
                maxS = pS;
                trs = cur;
            }
        }
    for(auto m: trs) st.push_back(m);
    return maxS;
}

double S(int N, Soltri& sol)
{
    sol.clear();
    return Sol(sol,N);
}

double Sn(int N)
{
    return N*sin(2*numbers::pi/N)/2;
}

int main(int argc, char * argv[])
{
    cout << fixed << setprecision(6);
    for(int N = 5; N < 50; ++N )
    {
        Soltri sol;
        double s = S(N,sol);
        cout << setw(2) << N << ":  "
            << setw(10) << s
            << setw(10) << s/Sn(N) << endl;
        for(auto t: sol)
        {
            for(auto p: t) cout << p << " ";
            cout << endl;
        }
    }
}

Расчет (не)покрытой площади для N=9*2^k-6

#include <iostream>
#include <iomanip>
#include <cmath>
#include <numbers>

using namespace std;

double Sn(int N)
{
    return N*sin(2*numbers::pi/N)/2;
}

double Area(int i, int j, int k, int N)
{
    return abs(sin(2*numbers::pi*(j-i)/N) + sin(2*numbers::pi*(k-j)/N) + sin(2*numbers::pi*(i-k)/N))/2;
}


int main()
{
    for(int k = 1; k < 21; ++k)
    {
        int N = (1 << k)*9-6;
        double s = 0;
        int L = 1;
        for(int i = k-1; i >= 0; --i)
        {
            int count = 3*(1<<i);
            s += count *Area(0,L,2*L,N);
            L = L*2 + 2;
        }
        s += Area(0,L,2*L,N);
        s = s/Sn(N);

        cout << N << "   " << setprecision(15) << 1.0-s << endl;
    }
}

Update 4

Все, проломил односекундный барьер для 500 :)

#include <vector>
#include <string>
#include <iostream>
#include <iomanip>
#include <cmath>
#include <numbers>
#include <array>
#include <map>

using namespace std;

struct Pnt
{
    double x,y;
};

Pnt pnt(int i, int N)
{
    Pnt p;
    p.x = cos(2*numbers::pi*i/N);
    p.y = sin(2*numbers::pi*i/N);
    return p;
}

using Trigon = array<int,3>;
using Soltri = vector<Trigon>;

struct Solution
{
    int N, count;
    double S = -1;
    Soltri sol;
};

// pair<int,int> - N и количество вершин (отсчет c 0)
map<pair<int,int>,Solution> Rep;

// Площадь треугольника
double Area(int i, int j, int k, int N)
{
    return abs(sin(2*numbers::pi*(j-i)/N) + sin(2*numbers::pi*(k-j)/N) + sin(2*numbers::pi*(i-k)/N))/2;
}

// pair<int,int> - начальная вершина и их количество
double S(pair<int,int> d, Soltri& st, int N)
{
    if (d.second < 3) return 0;
    if (auto sl = Rep.find(make_pair(N,d.second)); sl != Rep.end())
    {
        for(auto t: sl->second.sol)
        {
            t[0] += d.first;
            t[1] += d.first;
            t[2] += d.first;
            st.push_back(t);
        }
        return sl->second.S;
    }

    double maxS = 0;
    Soltri trs;

    int i = d.first, k = d.first + d.second-1;
    for(int j = max((i+k)/2,i+1); j < min((i+k)/2+3,k); ++j)
    {
        Soltri cur;
        Trigon t {i,j,k};
        cur.push_back(t);
        double pS = Area(i,j,k,N);
        pair<int,int> a[4];
        a[0] = make_pair(d.first,i - d.first);
        a[1] = make_pair(i+1,j - i - 1);
        a[2] = make_pair(j+1,k - j - 1);
        a[3] = make_pair(k+1,d.second - k - 1);

        pS += S(a[0],cur,N) + S(a[1],cur,N) + S(a[2],cur,N) + S(a[3],cur,N);

        if (pS > maxS)
        {
            maxS = pS;
            trs = cur;
        }
    }
    Solution sol;
    sol.N = N; sol.count = d.second; sol.S = maxS;
    for(auto m: trs)
    {
        st.push_back(m);
        for(int& tg: m) tg -= d.first;
        sol.sol.push_back(m);
    }
    Rep.insert(make_pair(make_pair(N,d.second),sol));

    return maxS;
}

double Sol(Soltri& st, int N)
{
    double maxS = 0;
    Soltri trs;
    int i = 0;
    for(int j = max(N/3 - 2,1); j < max(N/3+2,N); ++j)
        for(int k = max(2*N/3 - 2,j+1); k < max(2*N/3+2,N); ++k)
        {
            Soltri cur;
            Trigon t{i,j,k};
            cur.push_back(t);
            double pS = Area(i,j,k,N);
            pair<int,int> a[4];
            a[0] = make_pair(i+1,j - i - 1);
            a[1] = make_pair(j+1,k - j - 1);
            a[2] = make_pair(k+1,N - k - 1);
            pS += S(a[0],cur,N) + S(a[1],cur,N) + S(a[2],cur,N);
            if (pS > maxS)
            {
                maxS = pS;
                trs = cur;
            }
        }
    for(auto m: trs) st.push_back(m);
    return maxS;
}

double S(int N, Soltri& sol)
{
    sol.clear();
    return Sol(sol,N);
}

double Sn(int N)
{
    return N*sin(2*numbers::pi/N)/2;
}

int main(int argc, char * argv[])
{
    cout << fixed << setprecision(6);
    for(int N: {250,500})
    {
        Soltri sol;
        double s = S(N,sol);

        cout << setw(2) << N << ":  "
            << setw(10) << s
            << setw(10) << s/Sn(N) << endl;
        for(auto t: sol)
        {
            for(auto p: t) cout << p << " ";
            cout << endl;
        }
    }
}
→ Ссылка
Автор решения: amoonra

Спустя 3 дня я сумел оптимизировать алгоритм (на nodejs) так чтобы он мог работать с большими n. Вот как выглядит распределение n > 20. Действительно, отношение стремиться к 1, многоугольник заполняется все большим количеством треугольников, а ширина полос уменьшается.

n > 20 n > 170

Но идея о том, что нужно выбирать рекуррентно самый большой треугольник не верна, так как в некоторых случаях, суммарная площадь получается не максимальной.

Например при n = 14, при таком подходе площадь совпадает с тем вариантом, когда мы перебираем все несимметричные варианты. А вот при n = 15, распределение треугольников другое и суммарная площадь оказывается ниже.

введите сюда описание изображения

→ Ссылка
Автор решения: Kovel Pachnev

Очень интересно, как Вы получаете такие площади. Например, для десятиугольника по моим подсчётам на листочке и математическим формулам, я получил ответ с некоторой погрешностью от того, что мы должны получить (для удобства подсчёта разбил десятиугольник на части). На рисунке представляю Вам результаты, которые мы должны получить для n = 3, n = 10. Задание это из собеседования. введите сюда описание изображения

→ Ссылка