Задача взята со сборника задач Юркина А. Г.
Условие:
Найти расстояние между двумя произвольно заданными на плоскости отрезками.
Входные данные:
8 чисел, пары координат каждого конца отрезков.
Выходные данные:
Минимальное расстояние между отрезками.
Тесты:
[latex]x_1[/latex] | [latex]y_1[/latex] | [latex]x_2[/latex] | [latex]y_2[/latex] | [latex]x_3[/latex] | [latex]y_3[/latex] | [latex]x_4[/latex] | [latex]y_4[/latex] | Минимальное расстояние |
0 | 0 | 2 | 2 | 1 | 1 | 0 | 3 | 0 |
1 | 2 | 3 | 2 | 2 | 1 | 2 | 0 | 1 |
1 | 1 | 3 | 1 | 1 | 2.5 | 3 | 2.5 | 1.5 |
1 | -1 | 3 | -1 | 1 | 2.5 | 3 | 3.5 | 3.5 |
Решение:
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
#include <iostream> #include <cmath> #include <algorithm>//библиотека, содержащая функции min и max using namespace std; double perpendicularx(double x1,double y1, double x2, double y2, double x3, double y3) //функция, которая высчитывает значение абциссы проекции точки на прямую, содержащую отрезок { double x; x=(-x1*(y2-y1)*(y2-y1)+x3*(x2-x1)*(x2-x1)-y1*y2+y1*y1)/((x2-x1)*(x2-x1)-(y2-y1)*(y2-y1)); return x; } double perpendiculary(double x1,double y1, double x2, double y2, double x3, double y3, double x)//функция, которая высчитывает значение абциссы проекции точки на прямую, содержащую отрезок { double y; y=(x-x1)*(y2-y1)/(x2-x1)+y1; return y; } int main() { double x, y, x1, x2, x3, x4, y1, y2, y3, y4, temp; cin>>x1>>y1>>x2>>y2>>x3>>y3>>x4>>y4; double mini=sqrt((x3-x1)*(x3-x1)+(y3-y1)*(y3-y1)); //предположим, что минимальное значение, это расстояние от конца одного отрезка до конца другого temp=sqrt((x3-x2)*(x3-x2)+(y3-y2)*(y3-y2)); //для каждой пары концов разных отрезков высчитываем расстояние между ними if (temp<mini)//если оно меньше минимума mini=temp;//то меняем минимум temp=sqrt((x4-x1)*(x4-x1)+(y4-y1)*(y4-y1));//повторяем для каждой пары концов разных отрезков if (temp<mini) mini=temp; temp=sqrt((x4-x2)*(x4-x2)+(y4-y2)*(y4-y2)); if (temp<mini) mini=temp; x=perpendicularx(x1, y1, x2, y2, x3, y3);//получаем абциссу проекции конца одного отрезка на другой y=perpendiculary(x1, y1, x2, y2, x3, y3, x);//ординату if ((x2-x1)*(x2-x1)-(y2-y1)*(y2-y1)==0) //в случае, если равно нулю, абцисса будет равна бесконечности, а это возможно в том случае, если конец отрезка совпадает с его проекцией на другой отрезок { x=x3; // для корректных вычислений приравниваем абциссы и ординаты точки с её проекцией y=y3; } if (x>=min(x1,x2)&&x<=max(x1,x2)&&y>=min(y1,y2)&&y<=max(y1,y2)) //если проекция лежит на отрезке заданном { temp=sqrt((x3-x)*(x3-x)+(y3-y)*(y3-y));//то проверяем, будет ли расстояние от конца отрезка до её проекции меньше, чем текущий минимум if (temp<mini) mini=temp;//если да, то меняем значение минимума } x=perpendicularx(x1, y1, x2, y2, x4, y4); // далее повторяемя для трех других концов отрезков y=perpendiculary(x1, y1, x2, y2, x4, y4, x); if ((x2-x1)*(x2-x1)-(y2-y1)*(y2-y1)==0) { x=x4; y=y4; } if (x>=min(x1,x2)&&x<=max(x1,x2)&&y>=min(y1,y2)&&y<=max(y1,y2)) { temp=sqrt((x4-x)*(x4-x)+(y4-y)*(y4-y)); if (temp<mini) mini=temp; } x=perpendicularx(x3, y3, x4, y4, x1, y1); y=perpendiculary(x3, y3, x4, y4, x1, y1, x); if ((x4-x3)*(x4-x3)-(y4-y3)*(y4-y3)==0) { x=x1; y=y1; } if (x>=min(x3,x4)&&x<=max(x3,x4)&&y>=min(y3,y4)&&y<=max(y3,y4)) { temp=sqrt((x1-x)*(x1-x)+(y1-y)*(y1-y)); if (temp<mini) mini=temp; } x=perpendicularx(x3, y3, x4, y4, x2, y2); y=perpendiculary(x3, y3, x4, y4, x2, y2, x); if ((x4-x3)*(x4-x3)-(y4-y3)*(y4-y3)==0) { x=x2; y=y2; } if (x>=min(x3,x4)&&x<=max(x3,x4)&&y>=min(y3,y4)&&y<=max(y3,y4)) { temp=sqrt((x2-x)*(x2-x)+(y2-y)*(y2-y)); if (temp<mini) mini=temp; } cout<<mini<<endl; // выводим минимальное расстояние и переходим на новую строку return 0; } |
Описание решения:
При решении задачи использовались переменные типа [latex]double[/latex], так как в условии не поставлено ограничение на координаты концов отрезков, а тип [latex]double[/latex] существенно расширяет диапазон вводимых значений. Решение данной задачи сводится к тому, чтобы найти расстояния между концами разных отрезков и длины перпендикуляров, опущенных с концов одного отрезка на другой. Поэтому, условно разобьем задачу на два пункта:
1.Нахождение расстояния между концами двух отрезков.
2.Нахождение длины перпендикуляров, опущенных с концов одного отрезка на другой.
1.Расстояние между двумя точками [latex]M_1(mx_1,my_1), M_2(mx_2,my_2)[/latex] находится по формуле [latex]\sqrt{(mx_2-mx_1)^2+(my_2-my_1)^2}[/latex]. Сначала, переменной [latex]mini[/latex] присваиваем значение расстояния между первой парой концов отрезков:
20 |
mini=sqrt((x3-x1)*(x3-x1)+(y3-y1)*(y3-y1)); |
Далее, для всех остальных пар концов отрезков находим расстояние между ними, и если оно меньше чем [latex]mini[/latex], то меняем его значение.
После 4 проверок получаем минимальное расстояние между концами двух отрезков.
После этого переходим к пункту 2.
2. Для того, чтобы найти длину перпендикуляра, опущенного с конца одного отрезка на другой, необходимо решить систему, в которой первое уравнение — это уравнение прямой, содержащей концы отрезка, на который опускается перпендикуляр, а второе — уравнение прямой, содержащей этот перпендикуляр.
Продемонстрируем решение для отрезка с координатами [latex](x_1, y_1)[/latex] и [latex](x_2, y_2)[/latex], и точки с координатами [latex](x_3, y_3)[/latex]
Первое уравнение имеет вид [latex](x-x_1)/(x_2-x_1)=(y-y_1)/(y_2-y_1)[/latex], а второе : [latex]-(x_2-x_1)/(y_2-y_1)*(x-x_3)=y-y_3[/latex]. Решив эту систему, получаем, что
8 |
x=(-x1*(y2-y1)*(y2-y1)+x3*(x2-x1)*(x2-x1)-y1*y2+y1*y1)/((x2-x1)*(x2-x1)-(y2-y1)*(y2-y1)) |
и
14 |
y=(x-x1)*(y2-y1)/(x2-x1)+y1 |
Если делитель в [latex]x[/latex] равен нулю, то это означает, что проекция лежит на начале перпендикуляра, то есть они совпадают, и в таком случае присваиваем координатам проекции значения координат точки, с которой опускался перпендикуляр.
Получили абсциссу и ординату проекции точки на отрезок. Необходимо проверить, принадлежит ли эта проекция отрезку, для этого воспользуемся функциями [latex]min[/latex] и [latex]max[/latex], чтобы определить большие из координат отрезка, и операций сравнения:
37 |
if (x>=min(x1,x2)&&x<=max(x1,x2)&&y>=min(y1,y2)&&y<=max(y1,y2)) |
Если условия выполняются, то находим длину перпендикуляра по аналогии с расстоянием между концами отрезков, и сравниваем с [latex]mini[/latex]. Если длина меньше, то меняем значение минимума.
Так как все шаги нахождения повторяются 4 раза, то вынесем их в отдельные функции, которые высчитывают значения для каждой координаты проекции:
5 6 7 8 9 10 11 12 13 14 15 16 |
double perpendicularx(double x1,double y1, double x2, double y2, double x3, double y3) //функция, которая высчитывает значение абциссы проекции точки на прямую, содержащую отрезок { double x; x=(-x1*(y2-y1)*(y2-y1)+x3*(x2-x1)*(x2-x1)-y1*y2+y1*y1)/((x2-x1)*(x2-x1)-(y2-y1)*(y2-y1)); return x; } double perpendiculary(double x1,double y1, double x2, double y2, double x3, double y3, double x)//функция, которая высчитывает значение абциссы проекции точки на прямую, содержащую отрезок { double y; y=(x-x1)*(y2-y1)/(x2-x1)+y1; return y; } |
После выполнения всех шагов, получаем минимальное расстояние между двумя отрезками.
Выводим его на экран и переходим на новую строку с помощью команды [latex]endl[/latex].
Здесь код программы на сайте ideone.com.
Вы не указали, какие именно пары точек образуют отрезки. Я предположил, что первые две точки задают первый отрезок, а вторая пара точек второй. Правильно?
Тогда почему для [(0 0) (1 1)] и [(1 0) (2 1)] расстояние 0? Добавьте этот случай к тестам, пожалуйста.