Это задача с польской олимпиады по информатике под названием Pionek (PIO).
Учитывая набор неуникальных векторов, найдите максимально возможное евклидово расстояние относительно точки (0,0), которого вы можете достичь, перемещаясь с использованием векторов из набора. Каждый вектор можно использовать только один раз. Вернуть квадрат найденного расстояния.
Мы знаем, что для каждого вектора [x, y] из множества 10^-4 <= x,y <= 10^4
Для n, количества векторов в наборе, неравенство n <= 200 000 выполняется
Ограничение памяти 128MB
Пример ввода:
5 -> number of vectors
2 -2 -> [x, y] vector
-2 -2
0 2
3 1
-3 1
Наилучшего результата мы добьемся, выбрав векторы [0,2], [3,1], [2, -2]. Нашей конечной точкой назначения будет (5, 1), поэтому квадрат евклидова расстояния от (0,0) до (5,1) равен 26, и это действительный результат для этого ввода.
Я написал ниже решение грубой силы:
#include <iostream>
#include <vector>
using namespace std;
long long solve(const vector<pair<int, int>>& points, int i, int x, int y) {
if (i == points.size())
return 1LL * x * x + 1LL * y * y;
long long ans = 0;
ans = max(solve(points, i + 1, x, y), solve(points, i + 1, x + points[i].first, y + points[i].second));
return ans;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n;
cin >> n;
vector<pair<int, int>> points(n);
for (int i = 0; i < n; ++i)
cin >> points[i].first >> points[i].second;
cout << solve(points, 0, 0, 0);
}
Конечно, это слишком медленно (большинство тестовых случаев требуют времени выполнения менее 0,5 с или менее 1,5 с). Я могу использовать мемоизацию, но тогда превышаю лимит памяти.
За исключением грубой силы, я понятия не имею, что я могу сделать. Как мы можем решить ее с приемлемой временной сложностью, например O(nlogn)?
Было бы полезно, если бы вы описали, что вы подразумеваете под «грубой силой», потому что иногда это не совсем понятно.
@UlrichEckhardt Представленное здесь решение методом грубой силы просто пробует все подмножества векторов.
Сравните решение методом грубой силы с суммой всех векторов. (Я ожидаю, что он будет в том же полупространстве.)
@AhmedAEK Эта проблема была из конкурса (поэтому она, безусловно, разрешима), и существует четкое решение O (N log N).
Обратите внимание: хотя это и очевидно, но для любого набора векторов конечная точка одинакова для всех упорядочений этих векторов.





Есть один простой подход, который должен позволить упростить алгоритм. Идея состоит в том, что вы сначала решаете окончательное направление, а затем отбрасываете все, что не приносит чистой выгоды.
Более детально:
atan2() поможет вам в этом.Это, конечно, еще не оптимально. Чтобы улучшить его, вы не поворачиваете векторы (требуется много копий), а просто получаете к ним доступ по кругу (применяете оператор по модулю к индексу). Далее вы используете скользящее окно над векторами. Это означает, что как только вы достигли оптимального конечного вектора для определенного начального вектора (чтобы получить один результат), вы удаляете начальный вектор из суммы для следующего раунда. Это позволяет избежать сложения всех векторов, по которым два окна перекрываются.
Кстати: если реализовано с двумя дополнительными хаками, это должно работать за время O(n log n) и пространство O(1). Обоснование для времени: во-первых, сортировку можно выполнить за O(n log n). Затем для первого окна вы добавляете в среднем O(n/2) векторов. Затем для каждого из остальных n-1 окон каждый вектор будет добавлен один раз и удален один раз (в среднем O(1)), что составит O(n). Обоснование использования пространства: вы сохраняете только лучший результат, используя один вектор и два индекса для окна.
Сначала отсортировать?!!!?