Jiopik Ответов: 1

Максимальное количество пересечений прямоугольников на плоскости


У меня есть решение задачи, заданной n прямоугольниками (n <= 10^5), каждый вход прямоугольника представлен двумя входными линиями, первая строка имеет нижнюю левую точку, а вторая-верхнюю правую точку, (x,y) (-1000 <= x, y <= 1000). Вы должны найти максимум пересечений прямоугольников.

Код следующий, он работает немного быстро, но онлайн-судья моей школы, который я тоже отправляю этот код, терпит неудачу в последнем тестовом случае из-за ограничений по времени. Я больше не знаю, как его оптимизировать.

Выполнение вложенного цикла для проверки каждого прямоугольника оказалось асимптотически не подходящим, учитывая ограничение по времени 1С и объем памяти 256 МБ.

Входной Сигнал Образца:

2

1 1

2 2

3 3

4 4

Выход:

0


Входной Сигнал Образца:

4

1 4

9 6

2 1

6 3

3 1

7 6

5 4

7 8

Выход:

3

Что я уже пробовал:

//#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
//#include <cmath>
//#include <climits>
//#include <string>
#include <algorithm>

using namespace std;

struct Edge {
	enum Type
	{
		BEGIN,
		END
	};

	Type type;
	int abscissa;
	int top;
	int bottom;
};

struct Segment {
	int begin;
	int end;
	unsigned int intersections = 0;
};

bool operator<(Edge const& e1, Edge const& e2) {
	return e1.abscissa < e2.abscissa || (e1.abscissa == e2.abscissa && e1.type < e2.type);
}

bool operator<(Segment const& s1, Segment const& s2) {
	return s1.begin < s2.begin;
}

bool operator==(Segment const& s1, Segment const& s2) {
	return s1.begin == s2.begin;
}

//Start of edge's interval (between bottom and top) in segments via binary search
Segment* binary_search(Segment* Begin, Segment* End, int bottom) {

	while (End - Begin > 1) {

		auto mid = Begin + (End - Begin) / 2;

		if (mid->end == bottom) {
			return mid;
		}

		else if (mid->end < bottom)
			Begin = mid + 1;

		else
			End = mid;
	}
	return Begin;
}

int main() {

	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);

	int n, res = 0;
	cin >> n;

	Edge* edges = new Edge[2 * n];
	Segment* segments = new Segment[2 * n];

	for (int i = 0; i < n; i++) {
		int left, right, bottom, top;
		cin >> left >> bottom >> right >> top;

		edges[2 * i].type = Edge::BEGIN;
		edges[2 * i].abscissa = left;
		edges[2 * i].bottom = bottom;
		edges[2 * i].top = top;
		edges[2 * i + 1].type = Edge::END;
		edges[2 * i + 1].abscissa = right;
		edges[2 * i + 1].top = top;
		edges[2 * i + 1].bottom = bottom;

		segments[i * 2].begin = segments[i * 2].end = bottom;
		segments[i * 2 + 1].begin = segments[i * 2 + 1].end = top;
	}

	sort(edges, edges + 2 * n);
	sort(segments, segments + 2 * n);

	auto end = unique(segments, segments + 2 * n);

	for (auto i = segments + 1; i != end; i++)
		i[-1].end = i->begin;

	end--;
	for (int i = 0; i < 2 * n; i++) {// iterate over the edges 

		auto current = binary_search(segments, end, edges[i].bottom);

		while (current != end && current->end <= edges[i].top) {

			if (edges[i].type == Edge::BEGIN) {

				current->intersections++;

				if (current->intersections > res)
					res = current->intersections;
			}
			else
				current->intersections--;

			current++;
		}
	}

	if (res == 1)
		cout << 0;
	else
		cout << res;

	return 0;
}

Patrice T

Было бы неплохо дать точное требование и образец ввода и ожидаемого результата.

Jiopik

Я его обеспечил. Бизнес-школе INSEAD ходить в сегменте деревьев. Есть ли способ улучшить то, что есть сейчас?

1 Ответов

Рейтинг:
0

Patrice T

Цитата:
Код следующий, он работает немного быстро, но онлайн-судья моей школы, который я тоже отправляю этот код, терпит неудачу в последнем тестовом случае из-за ограничений по времени.

С сегодняшними компьютерами любой код "работает немного быстро" с небольшим набором данных, это не означает, что код эффективен, потому что проблемы возникают с большими наборами данных.
Цитата:
Я больше не знаю, как его оптимизировать.

Ваш код выглядит немного более продуманным для меня. Я не вижу причин поступать таким образом.
Мой подход был бы прямолинейным, без ребер или сегментов, но вместо этого простой список прямоугольников и с любым новым прямоугольником проверьте, пересекается ли он с предыдущим.
Это грубая сила, но мне она кажется проще.


Rick York

Я согласен с Патрис. При таком небольшом наборе данных нет необходимости в поиске или сортировке. Сравнение грубой силы было бы лучшим вариантом, я думаю.

Jiopik

Я согласен, что это было бы чрезмерно спроектировано, если бы n <= 10^4. Но это не так. И, возможно, вы должны были видеть, что даже с таким алгоритмом, ограничение по времени все еще не может быть пройдено. Любое предложение.

Patrice T

При моем подходе для n прямоугольников вы повторяете n*(n-1)/2 раза, примерно (n^2)/2.
Поскольку вы разбиваете прямоугольник на 2 ребра/сегмента и предполагаете, что двоичный поиск эффективен, я ожидаю, что ваш код будет повторяться в 2n*(2n-1)/2 шага, примерно (4n^2)/2.
Не уверен, что мой подход не будет быстрее.

Jiopik

Какова ваша временная сложность?

Patrice T

Количество прямоугольников для проверки во внутреннем цикле.
для любого прямоугольника x существует проверка прямоугольника x-1 во внутреннем цикле.
если 10 прямоугольников, то чеки будут 0+1+2+3+4+5+6+7+8+9
Этот люкс до y is y*(y+1)/2

KarstenK

Я тоже согласен. Двойной вызов сортировки является лучшим примером ненужного кода в виде итерации ребер 2*n.

Используйте только прямоугольники с точками и некоторые встроенные функции. Совет: подумайте об условиях завершения поиска и циклах, таких как out range.