Задача
Пересекаются ли отрезки. Для двух отрезков [latex]AB[/latex] и [latex]CD[/latex], заданных целочисленными координатами вершин на плоскости, определить имеют ли они общие точки.
Входные данные
Координаты концов отрезка[latex]AB[/latex] и [latex]CD[/latex].
Выходные данные
Пересекаются ли отрезки.
Тесты
Входные данные | Выходные данные | |||||||
[latex]x1[/latex] | [latex]y1[/latex] | [latex]x2[/latex] | [latex]y2[/latex] | [latex]x3[/latex] | [latex]y3[/latex] | [latex]x4[/latex] | [latex]y4[/latex] | |
1 | 0 | 2 | 1 | 1 | 0 | 2 | 0 | Отрезки пересекаются |
-1 | 1 | 2 | -2 | 0 | -2 | 1 | 3 | Отрезки пересекаются |
1 | 1 | 2 | 2 | 2 | 2 | 1 | 1 | Отрезки пересекаются |
-1 | 1 | -1 | 2 | 1 | 1 | 2 | 2 | Отрезки не пересекаются |
-2 | -1 | -2 | 3 | 0 | -1 | 0 | 3 | Отрезки не пересекаются |
-2 | 0 | 0 | 2 | 0 | -2 | 1 | 1 | Отрезки не пересекаются |
Код программы на C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#include <iostream> #include <cmath> using namespace std; int main() { double x1, y1, x2, y2, x3, y3, x4, y4; double Ua, Ub, numerator_a, numerator_b, denominator; cin >> x1 >> y1 >> x2 >> y2 >> x3 >> y3 >> x4 >> y4; denominator=(y4-y3)*(x1-x2)-(x4-x3)*(y1-y2); if (denominator == 0){ if ( (x1*y2-x2*y1)*(x4-x3) - (x3*y4-x4*y3)*(x2-x1) == 0 && (x1*y2-x2*y1)*(y4-y3) - (x3*y4-x4*y3)*(y2-y1) == 0) cout << "Отрезки пересекаются"; else cout << "Отрезки не пересекаются"; } else{ numerator_a=(x4-x2)*(y4-y3)-(x4-x3)*(y4-y2); numerator_b=(x1-x2)*(y4-y2)-(x4-x2)*(y1-y2); Ua=numerator_a/denominator; Ub=numerator_b/denominator; cout << (Ua >=0 && Ua <=1 && Ub >=0 && Ub <=1 ? "Отрезки пересекаются" : "Отрезки не пересекаются"); } return 0; } |
Код программы на Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import java.util.*; class Main{ public static void main (String[] args) throws java.lang.Exception{ Scanner scan = new Scanner(System.in); double Ua, Ub, numerator_a, numerator_b, denominator; double x1 = scan.nextDouble(); double y1 = scan.nextDouble(); double x2 = scan.nextDouble(); double y2 = scan.nextDouble(); double x3 = scan.nextDouble(); double y3 = scan.nextDouble(); double x4 = scan.nextDouble(); double y4 = scan.nextDouble(); denominator=(y4-y3)*(x1-x2)-(x4-x3)*(y1-y2); if (denominator == 0){ if ( (x1*y2-x2*y1)*(x4-x3) - (x3*y4-x4*y3)*(x2-x1) == 0 && (x1*y2-x2*y1)*(y4-y3) - (x3*y4-x4*y3)*(y2-y1) == 0) System.out.print("Отрезки пересекаются"); else System.out.print("Отрезки не пересекаются"); } else{ numerator_a=(x4-x2)*(y4-y3)-(x4-x3)*(y4-y2); numerator_b=(x1-x2)*(y4-y2)-(x4-x2)*(y1-y2); Ua=numerator_a/denominator; Ub=numerator_b/denominator; if (Ua >=0 && Ua <=1 && Ub >=0 && Ub <=1) System.out.print("Отрезки пересекаются"); else System.out.print("Отрезки не пересекаются"); } } } |
Решение
Пусть концы отрезков имеют координаты [latex]A(x_1,y_1)[/latex], [latex]B(x_2,y_2)[/latex], [latex]C(x_3,y_3)[/latex] и [latex]D(x_4,y_4)[/latex]. По имеющимся точкам выведем параметрические уравнения обоих отрезков: [latex]0\leq u \leq 1:[/latex] $$\begin{cases}x=u x_1+(1−u)x_2; \newline y=uy_1+(1−u)y_2\end{cases};$$ и [latex] 0\leq v \leq 1:[/latex]
$$\begin{cases}x=vx_3+(1−v)x_4 \newline y=vy_3+(1−v)y_4\end{cases}$$
В точке пересечения [latex]x[/latex] и [latex]y[/latex] должны совпадать. Значит выходит система двух линейных уравнений, которую нужно решить относительно [latex]u[/latex] и [latex]v:[/latex]
$$\begin{cases}ux_1+(1−u)x_2=vx_3+(1−v)x_4 \newline uy_1+(1−u)y_2=vy_3+(1−v)y4\end{cases}$$
Найдем определитель: [latex]denominator=(y_4-y_3)\cdot(x_1-x_2)-(x_4-x_3)\cdot(y_1-y_2).[/latex] Если он равен [latex]0[/latex], то прямые содержащие отрезки параллельны или совпадают. В этом случае проверим лежит ли вершина одного отрезка на другом, если она лежит, то отрезки пересекаются, иначе не пересекаются.
Если не [latex]0[/latex], то найдем решение по правилу Крамера:
$$\begin{cases}Ua=\frac{(x_4-x_2)\cdot(y_4-y_3)-(x_4-x_3)\cdot(y_4-y_2)}{denominator} \newline Ub=\frac{(x_1−x_2)\cdot(y_4−y_2)−(x_4−x_2)\cdot(y_1−y_2)}{denominator}\end{cases}$$
Если [latex]0\leq Ua \leq 1[/latex] и [latex]0\leq Ub \leq 1[/latex], то отрезки пресекаются, иначе отрезки не пересекаются.
Так, у меня нет сейчас карандаша и бумаги. Я прямо здесь поразмышляю, можно?
Зададим первый отрезок параметрически с параметром [latex]0 \leq u \leq 1[/latex]:[latex]\left.\begin{matrix}
x=ux_1+(1-u)x_2\\
y=uy_1+(1-u)y_2
\end{matrix}\right\}
[/latex]
Зададим второй отрезок параметрически с параметром [latex]0 \leq v \leq 1[/latex]:[latex]\left.\begin{matrix}
x=vx_3+(1-v)x_4\\
y=vy_3+(1-v)y_4
\end{matrix}\right\}
[/latex]
В точке пересечения [latex]x[/latex] и [latex]y[/latex] должны совпадать. Значит выходит система двух линейных уравнений, которую нужно решить относительно [latex]u[/latex] и [latex]v[/latex]:
[latex]\left.\begin{matrix}
ux_1+(1-u)x_2=vx_3+(1-v)x_4\\
uy_1+(1-u)y_2=vy_3+(1-v)y_4
\end{matrix}\right\}[/latex]
Пока всё логично? Тогда перепишем всё в общепринятом виде:
[latex]\left.\begin{matrix}
(x_1-x_2)u+(x_4-x_3)v=x_4-x_2\\
(y_1-y_2)u-v(y_4-y_3)=y_4-y_2
\end{matrix}\right\}[/latex]
Теперь найдем определитель: [latex]D=(x_1-x_2)(y_4-y_3)-(x_4-x_3)(y_1-y_2)[/latex]. Если он равен 0, то прямые содержащие отрезки параллельны. Этот случай нужно рассмотреть отдельно.
Если не 0, то найдем решение по правилу Крамера:
[latex]\left.\begin{matrix}
u=\frac{(x_4-x_2)(y_4-y_3)-(x_4-x_3)(y_4-y_2)}{D}\\
v=\frac{(x_1-x_2)(y_4-y_2)-(x_4-x_2)(y_1-y_2)}{D}
\end{matrix}\right\}[/latex]
Если оба результата не уходят меньше чем 0 или больше 1, то точка пересечения находится внутри отрезков. Иначе пересечения нет.
Кажется так.
Теперь просьба к Вам. Проверьте мои рассуждения и скажите, выдаёт ли Ваша программа такой же ответ.
Вроде да. А зачем тогда swap() в коде?
Пояснения переделали, это хорошо. На вопросы не отвечаете — это плохо.
Здравствуйте, Игорь Евгеньевич. Спасибо что не теряете веру в меня и за Ваше внимание. Функция swap () нужна по причине описанной в решении «Поскольку данная формула работает только для горизонтальных отрезков, в случае когда отрезок вертикален мы его перевернем.»
Да, Вы правы.
Эта фраза мне тоже не нравится.
Просто я её почему-то не заметил.
Игорь Евгеньевич, подскажите, пожалуйста, что мне делать с этой задачей, а именно: что мне конкретно в ней исправлять?
Вам нужно принять решение самому. У Вас есть вся информация.
Игорь Евгеньевич, я не понимаю. Без функции swap () программа не проходит тест с вертикальной прямой (-1 1 -1 2 1 1 2 2).
Даниил, я тоже не понимаю. В комментарии я привел все формулы….
Ладно, написал программу, реализующую ту часть вычислений, что и у Вас. Все работает нормально.
Исправил, Игорь Евгеньевич. Программа не работала из-за ошибки в формуле.
Вы не обратили внимание на текст «Если он равен 0, то прямые содержащие отрезки параллельны. Этот случай нужно рассмотреть отдельно.»
Отрезки в этом случае могут иметь общие точки (пересекаться и даже совпадать) или не пересекаться. Этот случай нужно действительно рассмотреть отдельно, а не просто «не пересекаются» и все.
Посмотрите , например, вариант
0 0 0 2
0 1 0 3
Исправил, Игорь Евгеньевич. Сделал проверку при denominator = 0.
Не бросайте задачу — мы уже очень близки к финишу 🙂
— Не нужно четыре условных оператора для случая нулевого определителя. Нужен один со сложным условием с союзом «или» — ||
— Нельзя извлекать корень и сравнивать на равенство. Из-за ошибок округления может возникнуть «ложно отрицательное» срабатывание.
— Как проверить проще? Вы ведь уже знаете, что все 4 точки лежат на одной прямой! Значит «между» будет выполняться для обоих координат.
— Корректорское замечание. После текста «Пусть концы отрезков имеют координаты…» напишите буквы A. B, C, D перед каждой скобкой, чтобы ясно было где чьи координаты.
— И еще одно. «Ссылки Ideone.» Не очень понятно. Лучше написать что-то вроде «Выполнить программу».
А давайте без этого System.exit(0); обойдемся? Условные операторы вполне позволяют…
Даже после того, как я поправил формулы остается глобальная проблема — оба решения не проходят тесты. Одна из проблем сразу видна — ввод целых координат не через long long, а через double.
Как я понял, программа должна проверять, пересекаются ли отрезки. Уточняю, отрезки, а не прямые. Тогда почему при использовании данной программы (которая под с++) если отрезки лежать на одной прямой, но не пересекаются (для примера я взял первый отрезок с координатами x1 = 10, y1 = 10, x2 = 20, y2 = 10 и второй отрезок с координатами x3 = 30, y3 = 10, x4 = 40, y4= 10) то программа выдаёт, что они пересекаются, хотя по факту они не пересекаются.
Вы правы. Но на сайте можно найти правильные решение этой задачи. Например, https://cpp.mazurok.com/e-olymp-839.
А здесь (https://cpp.mazurok.com/%d1%8e2-15) подробно указывается взаимное положение отрезков.