Одной из наиболее известных задач на геометрию - задача на построение выпуклой оболочки(ссылка на задачу внизу). Кто-то делает его алгоритмом Джарвиса за квадрат, кто-то - обычным алгоритмом Грэхема за n log n, но с использованием вещественных чисел.
Однако всё же есть несколько способов обойтись без ошибки по времени и вещественных чисел. Одним из них является алгоритм Грэхема-Эндрю. Алгоритм является оптимальным и использует только сложение, умножение и вычитание.
Суть алгоритма довольно несложная, если вы хорошо понимаете алгоритм Грэхема. Давайте найдём самую левую нижнюю и самую правую верхнюю точки из данных нам. Пусть это будут точки А и В соответственно. Дальше мы проведём прямую через эти точки. Так, мы разделили прямую на две части - верхнюю и нижнюю(Точки на этой прямой, кроме А и В, мы не учитываем ни в какую из частей. Сами же точки А и В войдут в обе части).Дальше мы сортируем все точки по абсциссе, а при их равенстве - по ординате. Всё, что нам осталось сделать - использовать алгоритм Грэхема для построения сначала верхней, а затем и нижней части оболочки.
Код(для задачи, указанной ниже).
#include <bits/stdc++.h>
#define ld long double
#define int long long
#define pb push_back
#define F first
#define S second
#define endl '\n'
using namespace std;
int32_t main ()
{
///Алгоритм Грэхема-Эндрю, написан и объяснён _GreatArthur_
cin.tie(0);
cout.tie(0);
ios_base::sync_with_stdio(0);
int n;
cin >> n;
int copy = n;
long double x[n], y[n];
///Используем map для удаления одинаковых точек.
map < pair < ld, ld >, int > mp;
for (int i = 0; i < n; i++)
{
cin >> x[i] >> y[i];
mp[{x[i], y[i]}]++;
if (mp[{x[i], y[i]}] > 1)
{
i--;
n--;
}
}
///Отдельно проверяем частный случай.
if (n == 1)
{
cout << 1 << endl << x[0] << ' ' << y[0];
return 0;
}
///Находим номера точек А и В.
int numA = 0, numB = 0;
for (int i = 0; i < n; i++)
{
if (x[numA] > x[i] || (x[numA] == x[i] && y[numA] > y[i]))
numA = i;
if (x[numB] < x[i] || (x[numB] == x[i] && y[numB] < y[i]))
numB = i;
}
///Создаём два множества для верхней и нижней части оболочки.
vector < int > up, down;
up.pb(numA);
down.pb(numA);
///Сортируем точки по абсциссе(v[i].F), а при их равенстве - по ординате(v[i].S.F). Чтобы понять номер точек после сортировки, также храним их номер(v[i].S.S).
vector < pair < int, pair < int, int > > > v;
for (int i = 0; i < n; i++)
v.pb({x[i], {y[i], i}});
sort(v.begin(), v.end());
int ii = 0;
///Ищем верхнюю часть оболочки.
do
{
ii++;
if ((x[v[ii].S.S] - x[numA]) * (y[numB] - y[numA]) - (y[v[ii].S.S] - y[numA]) * (x[numB] - x[numA]) <= 0 && v[ii].S.S != numB) continue;
///Для каждой точки мы проверяем, сверху ли она или её номер - numB.Если да, то мы делаем алгоритм ниже, иначе - просто игнорируем её.
while (up.size() > 1 && (x[v[ii].S.S] - x[up[up.size() - 2]]) * (y[up[up.size() - 1]] - y[up[up.size() - 2]]) - (y[v[ii].S.S] - y[up[up.size() - 2]]) * (x[up[up.size() - 1]] - x[up[up.size() - 2]]) >= 0)
up.pop_back();
up.pb(v[ii].S.S);
}
while (ii < v.size() - 1);
ii = 0;
///Ищем нижнюю часть оболочки.
do
{
ii++;
if ((x[v[ii].S.S] - x[numA]) * (y[numB] - y[numA]) - (y[v[ii].S.S] - y[numA]) * (x[numB] - x[numA]) >= 0 && v[ii].S.S != numB) continue;
///Для каждой точки мы проверяем, снизу ли она или её номер - numB.Если да, то мы делаем алгоритм ниже, иначе - просто игнорируем её.
while (down.size() > 1 && (x[v[ii].S.S] - x[down[down.size() - 2]]) * (y[down[down.size() - 1]] - y[down[down.size() - 2]]) - (y[v[ii].S.S] - y[down[down.size() - 2]]) * (x[down[down.size() - 1]] - x[down[down.size() - 2]]) <= 0)
down.pop_back();
down.pb(v[ii].S.S);
}
while (ii < v.size() - 1);
///Выводим оболочку.
cout << up.size() + down.size() - 2 << endl;
if (copy % 2 == 0)
{
for (int i = 0; i < up.size(); i++)
cout << x[up[i]] << ' ' << y[up[i]] << endl;
for (int i = down.size() - 2; i >= 1; i--)
cout << x[down[i]] << ' ' << y[down[i]] << endl;
}
else
{
for (int i = 1;i <= down.size() - 2; i++)
cout << x[down[i]] << ' ' << y[down[i]] << endl;
for (int i = up.size() - 1;i >= 0;i--)
cout << x[up[i]] << ' ' << y[up[i]] << endl;
}
return 0;
}
Сдать задачу:
https://informatics....hapterid=1877#1
Сообщение отредактировал LVP: 17 октября 2020 - 08:04