Время на прочтение
4 мин
Количество просмотров 11K
К нам в техподдержку часто приходит вопрос: «Как посчитать суммы длин отрезков (участков трубопровода, элементов электрических схем и т.п.) в чертеже?». Существует масса способов решения этой задачи, в сегодняшней публикации мы рассмотрим реализацию приложения на MultiCAD.NET API, суммирующего длины, которое работает в nanoCAD, AutoCAD и ZWCAD. В качестве примера мы возьмем задачу определения суммарной длины труб в схеме водоснабжения и рассмотрим два варианта выбора элементов для подсчета: пользовательский и по созданному фильтру.
Определение суммы длин отрезков, выбранных пользователем
Прежде чем приступить к определению длины отрезка, необходимо определить, что же такое отрезок в MultiCAD.NET. Отрезок является стандартным примитивом наряду с окружностью, текстом, сплайном и др. Для представления отрезка в базе данных чертежа используется класс DbLine
из пространства имен всех примитивов Multicad.DatabaseServices.StandardObjects
.
Объекты DbLine
в качестве свойств содержат начальную и конечную точку, но не содержат информации о длине отрезка. Конечно, координат точек отрезка достаточно для вычисления его длины, но удобнее будет использовать его геометрическое представление — объект класса LineSeg3d
(доступ к которому обеспечивает свойство DbLine.Line
) и его свойство Length
для получения длины:
double length = line.Line.Length;
Итак, рассмотрим первый вариант приложения, когда пользователю предлагается самостоятельно выбрать отрезки для вычисления итогового значения длины. Для реализации пользовательского выбора объекта будет использоваться метод SelectObjects
класса менеджера объектов McObjectManager
:
public static McObjectId[] SelectObjects(string sPromt);
Метод выводит подсказку в консоль и позволяет пользователю самому выбирать объекты, ID выбранных объектов записываются в массив. Затем производится распознавание элементов массива, и для объектов, которые являются отрезками, получаем длину и инкрементируем результат. Общий вид команды, реализующий эту процедуру:
CommandMethod("LineLengthSum", CommandFlags.NoCheck | CommandFlags.NoPrefix)]
public void LineLengthSum()
{
// Получаем объекты выбором на чертеже
McObjectId[] idSelecteds = McObjectManager.SelectObjects("Выберите объекты типа линия");
if (idSelecteds.Length == 0)
{
MessageBox.Show("Объекты не выбраны");
return;
}
double lengthSum = 0;
foreach (McObjectId currID in idSelecteds)
{
// Получаем объект по ID
McObject currObj = currID.GetObject();
// Распознавание типа объекта
if (currObj is DbLine)
{
lengthSum += (currObj as DbLine).Line.Length;
}
}
MessageBox.Show(lengthSum.ToString(), "Длина всех выбранных отрезков:", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
Кроме отрезков прямых на чертежах используются полилинии, которые представляют собой совокупность отрезков и/или дуговых элементов. Получить длину полилинии можно аналогично, использую геометрическое представление примитива — класс Polyline3d
через свойство Polyline
:
double length = polyline.Polyline.Length;
Автоматический подсчет суммарной длины линий
На практике, когда чертеж содержит большое число элементов и требуется исключить ошибки пользовательского ввода, объекты могут быть выбраны автоматически, используя фильтр объектов. При создании фильтра указываются необходимые критерии: область выбора объектов (листы, слои, документы, область, и пр.) и типы объектов.
Например, для того, чтобы выбрать все линии на конкретном слое используется фильтр с указанием имени слоя:
ObjectFilter filter = ObjectFilter.Create(true);
filter.AddLayer("Холодная вода");
filter.AddType(typeof(DbLine));
List<McObjectId> ids = filter.GetObjects();
Наиболее часто встречающийся на практике пример применения автоматического подсчета суммарной длины линий — формирование отчета по типу труб на схеме водоснабжения.
В нашем примере схема трубопровода организована таким образом, что трубы каждого типа расположены на отдельных слоях: «Контур 1» и «Контур 2».
Следующая команда формирует текстовый отчет с указанием всех типов труб, расположенных на отдельных слоях и их суммарной длины.
[CommandMethod("createReport", CommandFlags.NoCheck | CommandFlags.NoPrefix)]
public void createReport()
{
List<String> reportStrings = getLengthSumByLayer();
if (reportStrings.Count == 0)
{
MessageBox.Show("Схема не содержит элементов линий и полилиний");
return;
}
// Отступ для вывода строки отчета
int indent = 0;
// Заголовок отчета
DbText caption = new DbText();
caption.Text = new TextGeom("Длина труб по типу", new Point3d(0, indent, 0), Vector3d.XAxis, "Standard", 10);
caption.DbEntity.AddToCurrentDocument();
foreach (String str in reportStrings)
{
indent -= 10;
DbText reportText = new DbText();
reportText.Text = new TextGeom(str, new Point3d(0, indent, 0), Vector3d.XAxis, "Standard", 6);
reportText.DbEntity.AddToCurrentDocument();
}
}
Подсчет суммарной длины и заполнение строк отчета производится в методе getLengthSumByLayer()
, код которого представлен ниже:
public List<String> getLengthSumByLayer()
{
List<String> reportStrings = new List<String>();
// Получаем все слои на чертеже
List<string> layers = McObjectManager.CurrentStyle.GetLayers();
foreach (string layerName in layers)
{
ObjectFilter filter = ObjectFilter.Create(true).AddType(typeof(DbLine)).AddType(typeof(DbPolyline)).AddLayer(layerName);
List<McObjectId> idSelected = filter.GetObjects();
if (idSelected.Count != 0)
{
double lengthSum = 0;
foreach (McObjectId currID in idSelected)
{
// Получаем объект по ID
McObject currObj = currID.GetObject();
// Распознавание типа объекта
if (currObj is DbLine)
{
lengthSum += (currObj as DbLine).Line.Length;
}
else if (currObj is DbPolyline)
{
lengthSum += (currObj as DbPolyline).Polyline.Length;
}
}
// Если суммарная длина отрезков и полилиний на слое ненулевая, то добавляем в текст отчета
if (lengthSum != 0)
{
reportStrings.Add(layerName.ToString() + ": " + lengthSum.ToString());
}
}
}
return reportStrings;
}
После выполнения данной команды на чертеж будет добавлен отчет вида:
Подробную процедуру загрузки MultiCAD.NET приложений вы можете найти в нашей статье Пошаговый обзор: единое MultiCAD.NET приложение в nanoCAD, AutoCAD, ZWCAD.
Обсудить статью можно также и на нашем форуме.
Перевод статьи на английский: MultiCAD.NET: Calculating the total length of lines.
Суммарная длина
Cтраница 1
Суммарная длина по фронту в зависимости от мощности – от 1400 до 2400 мм, глубина – от 400 до 800 мм.
[1]
Суммарная длина участков А.
[2]
Суммарная длина L3 ( G) ребер, смежных только нефиксированным вершинам, которая определяется как суммарная длина ребер стандартного графа, построен-ногр на п – q вершинах, и оставшихся неучтенных ребер графа.
[3]
Суммарная длина всех подпрограмм HIPACK составила на CDC-6500 5130 слов, в то время как решение задачи, связанной с заведением, заполнением и распечаткой одной одномерной и одной друхмерной гистограмм с использованием НВООК, эта система потребовала 11400 слов. Так как подпрограммы HIPACK независимы друг от друга, легко организовать их выполнение в оверлейном режиме, что позволит сократить объем требуемой памяти ЭВМ в несколько раз. Кроме того, в зависимости от вида решаемой задачи пользователь из пакета подпрограмм HIPACK может выбрать и использовать только те подпрограммы, которые ему нужны.
[4]
Суммарная длина всех дефектов типа потеря металла составляет 833 098 м, что составляет 0 55 % протяженности обследованного участка трубопровода. Отсюда следует, что на болотах дефектов коррозии больше, чем на остальных участках.
[6]
Суммарная длина горизонтальных участков труб должна быть не более 3 м, так как с увеличением их длины температура продуктов сгорания и величина тяги уменьшаются.
[7]
Суммарная длина всех перпендикуляров, опущенных из любой точки, находящейся внутри равностороннего треугольника, на каждую из его сторон является величиной постоянной, равной его высоте.
[9]
Суммарная длина трех отрезков, отсеченных на сторонах равностороннего треугольника прямыми, параллельными его сторонам, проведенными через любую точку внутри его, является величиной постоянной, равной длине стороны треугольника.
[11]
Суммарная длина по фронту в зависимости от мощности – от 1400 до 2400 мм, глубина – от 400 до 800 мм.
[12]
Суммарная длина светильников превышает длину помещения: необходимо или применить более мощные лампы ( у которых световой поток на единицу длины больше), или увеличить число рядов, или компоновать ряды из сдвоенных, строенных светильников.
[13]
Суммарная длина светильников равна длине помещения: задача решается установкой непрерывного ряда светильников.
[14]
Суммарная длина светильников меньше длины помещения: принимается ряд с равномерно распределенными вдоль него разрывами А, между светильниками.
[15]
Страницы:
1
2
3
4
5
Отрезок, равный 40 см, разделён на четыре неравных отрезка. Расстояние между серединами крайних отрезков равно 28 см. Найдите расстояние между серединами средних отрезков в сантиметрах. А я, как обычно, через переменные и уравнения. Обозначим отрезки k, l, m, n, расположенные в таком порядке. k и n – крайние отрезки. Сумма длин всех отрезков равна 40 см. Получаем уравнение: k+l+m+n=40 (1) Расстояние между серединами крайних отрезков, равное 28 см, составляют половины крайних отрезков и 2 средних. Получаем такое уравнение: k/2+l+m+n/2=28 Домножим на 2, чтобы уйти от знаменателей. k+2l+2m+n=56 (2) Вычтем из (2) уравнения (1) уравнение. Получим: l+m=16 см – Это длина двух средних отрезков вместе, а нам нужно найти расстояние между их серединами, оно будет составлять половину этой длины двух отрезков, то есть: 16/2=8 см. Ответ: 8 см. автор вопроса выбрал этот ответ лучшим Лара Изюминка 6 месяцев назад Попробуем разобраться в задаче. Итак, известно, что весь отркзок 40. И состоит он из 4 неравных отрезков. ХУZW. Расстояние между серединами крайних отрезков известно, оно равно 28. Далее можно заметить, что 1/2хплюс у плюс z плюс 1/2 w равно 28. Тогда можем найти разницу между всем отрезком и этой известной его частью она будет равна 1/2х плюс 1/2 z=40-28=12. Тогда сумма отрезков крайних х и z будет равна в два раза больше, то есть 12*2=24. Тогда сумма средних отрезков равна 40-24=16. Тогда расстояние между их серединами будет равно их сумме поделенной на 2, то есть 16/2=8. Ответ на эту задачку 8. Nasos 6 месяцев назад Поскольку расстояние между серединами крайних отрезков равно 28см, то по краям остаётся в сумме: 40см – 28см = 12см, а это не что иное, как половина суммы длины обоих крайних отрезков, следовательно, из суммарная длина равна 24см, а тогда на суммарную длину двух средних отрезков приходится: 40см – 24см = 16см, откуда не трудно посчитать, что расстояние между их серединами будет вдвое меньше, то есть 8см. Грустный Роджер 6 месяцев назад Ну пусть расстояние от середины самого левого куска до левого конца отрезка х см. Тогда расстояние от середины самого правого куска до правого конца отрезка – 12-х см (12 – это 40-28). Значит, сумма длин двух крайних отрезков составляет 24 см. Тогда сумма длин двух средних – 16 см. И расстояние между их серединами – половина от этого, то есть 8 см. Знаете ответ? |
Смотрите также: Что такое равновеликие фигуры (куб, квадрат, многоугольник)? Для чего нужна математика, геометрия, физика в программировании? Как отложить на данном луче от его начала отрезок, равный данному? Как найти вписанный угол ACB, если дуга BC составляет 80 градусов? Как найти длину отрезка BD, если SO = 35, SD = 37? Как найти величину угла OAB, если угол OCD равен 30 градусам? По каким учебникам изучают математику израильские школьники? Как решить: В четырехугольнике АВСD противоположные стороны не параллельны? Диагональ АС параллелограмма АВСD 21, от верш. В до диаг. 12. Чему равна S? Как найти площадь треугольника ABM (см.)? |
27 задание – ScanLine (сканирующая прямая)
Введение
Сейчас в базе заданий ЕГЭ задач на отрезки мало, но они вполне могут быть на экзамене. Для начала разберемся, что такое отрезки и с чем их едят?
Задачи на отрезки обычно описаны примерно так:
Вам дано множество отрезков:
- найдите длину которую они суммарно занимают на прямой
- найдите суммарную длину для нескольких видов отрезков на прямой
- Сожмите множество, соединяя или удаляя некоторые отрезки, чтобы они покрывали ту же область на прямой
- найдите максимальное количество отрезков, пересекающихся друг с другом на участке прямой(длины > 0)
- найдите количество участков прямой, где пересекаются ровно X отрезков (где X натурально число)
Ещё 3 вида задач, которые маловероятны на ЕГЭ, но которые умеет решать ScanLine
- найдите количество точек принадлежащих прямоугольнику с координатами x1, y1, x2, y2
- найдите площадь поверхности, которую занимают множество прямоугольников
- найдите максимальное количество пересекающихся отрезков на плоскости
Идея сканирующей прямой
Давайте представим каждый отрезок как пару точек: его начало и конец. После отсортируем наши точки,
чтобы по ним пробежаться “сканирующей прямой” и обработать.
segments = [(1, 10), (2, 5), (3, 15)] points = [] for start, end in segments: points += [(start, 1)] # 1 - начало отрезка points += [(end, -1)] # -1 - конец отрезка points.sort() # [(1, 1), (2, 1), (3, 1), (5, -1), (10, -1), (15, -1)] # Важное замечание по поводу сортировки, в НЕКОТОРЫХ случаях сортировать надо так, # чтобы конец (-1) был перед началом (1). Но в каких-то задачах наоборот. # Встроенная сортировка будет в случае (5, 1) и (5, -1) сортировать как [(5, -1), (5, 1)] for point in points: pass # сюда обработчик (сканирующая прямая)
Суммарная длина занимаемая всеми отрезками
Сформулируем задачу в формате ЕГЭ
Имеется набор данных, состоящий из отрезков лежащих на прямой (отрезок это пара целых чисел).
Вам требуется найти суммарную длину прямой, которую эти отрезки покрывают.
Гарантируется, что (ОТВЕТ В ЗАДАЧЕ ВЕРНЫЙ) в отрезках координата начала меньше координаты конца.
Программа должна напечатать одно число – длину, соответствующую условиям задачи.Входные данные.
Даны два входных файла (файл A и файл B),
каждый из которых содержит в первой строке количество пар N (1 ≤ N ≤ 1e6).
Каждая из следующих N строк содержит два натуральных числа в диапазоне
от -1e9 до 1e9 включительно.
Ввод | Ответ |
---|---|
5 | |
1 4 | |
2 3 | |
5 7 | |
8 9 | |
4 5 | 7 |
Пояснение к примеру
Итоговая суммарная длинна равна 7-1 + 9-8 = 6 + 1 = 7
Для такой задачи метод сканирующей прямо как раз нам подходит!
with open("27.txt", "r") as file: n = int(file.readline()) segments = [list(map(int, file.readline().split())) for _ in range(n)] points = [] for start, end in segments: points += [(start, 1)] # 1 - начало отрезка points += [(end, -1)] # -1 - конец отрезка points.sort() amount_of_segments = 0 # нынешнее количество пересекающихся отрезков length = 0 # суммарная длинна prev_p = 0 # координаты последней точки for p, t in points: # координата точки и её тип (начало/конец) if amount_of_segments > 0: # если хотя бы один отрезок есть length += p - prev_p # добавляем разницу координат в длину prev_p = p amount_of_segments += t # добавляем тип (смотри пояснения) print(length)
Давайте разбираться что тут происходит!
- Мы ввели счётчик, который отвечает за количество пересекающихся отрезков на нашем промежутке. Он является ВАЖНОЙ деталью этого алгоритма
- Если счётчик больше 0, значит что у нас хотя бы один отрезок лежит на промежутке от
prev_p
доp
, значит эту длину можно спокойно вносить в нашу сумму amount_of_segments += t
, эта строка означает что если мы встретили начало отрезка, то мы увеличиваем счётчик отрезков на 1, а если встретили конец, то -1 (на 1 отрезок стало меньше). Именно по этому мы так поделили наши отрезки на (1 – начало), (-1 – конец).
Если основная идея ясна, идём дальше!
Суммарная длина нескольких видов отрезков
Имеется упорядоченный набор данных, состоящий из отрезков, лежащих на прямой (отрезок это тройка целых чисел, где 1 и 2 число – координаты начала и конца, 3 число – тип отрезка).
Отрезки упорядочены по времени их появления, то есть отрезки, идущие раньше могут быть перекрыты отрезками, идущими позже (не может одновременно быть 2-х и более различных типов отрезков на одном участке прямой).
Вам требуется найти для каждого типа суммарную длину прямой, которую отрезки этого типа покрывают.Значение S – это произведение этих найденных сумм.
Гарантируется, что (ОТВЕТ В ЗАДАЧЕ ВЕРНЫЙ) в отрезках координата начала меньше координаты конца.
Программа должна напечатать одно число – значение S.Входные данные.
Даны два входных файла (файл A и файл B),
каждый из которых содержит в первой строке количество троек N (1 ≤ N ≤ 1e6).
Каждая из следующих N строк содержит три натуральных числа в диапазоне
от -1e9 до 1e9 включительно.
Ввод | Ответ |
---|---|
5 | |
1 4 1 | |
2 3 2 | |
5 7 1 | |
8 9 2 | |
4 5 3 | 8 |
Пояснение к примеру
Произведение – 8:
- Суммарная длина типа1 (красных) – 4
- Суммарная длина типа2 (зелёных) – 2
- Суммарная длина типа3 (синих) – 1
from collections import defaultdict with open("27.txt", "r") as file: n = int(file.readline()) segments = [list(map(int, file.readline().split())) for _ in range(n)] points = [] for start, end, color in segments: points += [(start, 1, color)] # 1 - начало отрезка, ДОБАВЛЯЕМ ЦВЕТ так как он нам важен points += [(end, -1, color)] # 1 - конец отрезка, ДОБАВЛЯЕМ ЦВЕТ так как он нам важен points.sort() colors = [] # стек где последний элемент - нынешний тип отрезка color_length = defaultdict(int) # словарь цвет-суммарная длина этого цвета prev_p = 0 # координаты последней точки for p, t, c in points: # координата, тип(начало/конец), цвет if colors: # если есть хотя бы один начавшийся и пока не закончившийся отрезок color_length[colors[-1]] += p - prev_p # добавляем к сумме длин отрезков этого цвета промежуток от предыдущей точки до текущей prev_p = p if t == 1: # если текущая точка - начало отрезка colors.append(c) # кладем его цвет на верх стека else: # если конец colors.pop() # удаляем цвет с вершины стека(кстати, colors[-1] будет равен с, подумайте почему) S = 1 # Считаем значение S for key, val in color_length.items(): S *= val print(S) # Выводим его
Идея очень похожа на решение задачи с одним типом отрезков:
- Заводим стек (если неясно что это, дайте знать, я опишу базовые структуры данных) типов. Почему именно стек? В контексте этого решения на вершине стека всегда будет лежать последний начавшийся отрезок. Этот отрезок можно легко “закончить”, сняв его с вершины стека.
- Заводим словарь, в котором будем хранить ответы
тип:суммарная_длинна_этого_типа
- В случае если на стеке уже лежит хотя бы один цвет (
if colors
), прибавляем к ответу расстояние от начала предыдущего отрезка до текущей точки. - Если текущая точка – начало отрезка, кладем его цвет на стек. Иначе – удаляем.
Я открыта для вопросов, так что если они возникли, пишите в группе или в личку
Сжатие множества отрезков
Имеется набор данных, состоящий из отрезков лежащих на прямой (отрезок это пара целых чисел).
Вам требуется переделать множество так, чтобы оно содержало в себе как можно меньше отрезков, но чтобы оно покрывало такие же промежутки прямой, что и оригинальное множество.
Гарантируется, что (ОТВЕТ В ЗАДАЧЕ ВЕРНЫЙ) в отрезках координата начала меньше координаты конца.
Программа должна напечатать одно число – размер минимального множества.Входные данные.
Даны два входных файла (файл A и файл B),
каждый из которых содержит в первой строке количество пар N (1 ≤ N ≤ 1e6).
Каждая из следующих N строк содержит два натуральных числа в диапазоне
от -1e9 до 1e9 включительно.
Ввод | Ответ |
---|---|
5 | |
1 4 | |
2 3 | |
5 7 | |
8 9 | |
4 5 | 2 |
Пояснение к примеру
Мы можем переделать входные данные на множество размера 2 с отрезками: 1-7 и 8-9
Посмотрим как тут ScanLine может нам помочь:
with open("27.txt", "r") as file: n = int(file.readline()) segments = [list(map(int, file.readline().split())) for _ in range(n)] points = [] for start, end, color in segments: points += [(start, 1)] # 1 - начало отрезка points += [(end, -1)] # 1 - конец отрезка points.sort(key=lambda a: [a[0], -a[1]]) # ВАЖНО, нам надо чтобы начало шло первее конца compressed = [] amount_of_segments = 0 previous_start = 0 for p, t in points: if amount_of_segments == 0 and t == 1: # если сейчас 0 отрезков, и появился новый previous_start = p # записываем новый старт if amount_of_segments == 1 and t == -1: # если сейчас 1 отрезок, и мы встретили конец compressed.append([previous_start, p]) # добавляем сжатый отрезок в список amount_of_segments += t print(len(compressed))
Важные моменты в этой идее:
- Нам нужно изменить сортировку точек. Это надо чтобы при пересечении отрезков в виде 1-4 4-6, у нас не обнулялся счётчик с количеством. Иначе в этом случае мы получим ответ не 1 (1-6), а 2 (1-4 и 4-6)
compressed
это список полученных сжатых отрезков. В данной задаче нам хранить сами отрезки необязательно, но если поменять условие нанайдите колличество отрезков умноженное на длину максимального
или что-то ещё сложнее. То наличие этого списка вам поможет легко решить любую модификацию.
Максимальное количество отрезков пересекающихся друг с другом
Имеется набор данных, состоящий из отрезков, лежащих на прямой (отрезок это пара целых чисел).
Вам требуется найти максимальное количество отрезков, которые пересекаются друг с другом (под пересечением имеется ввиду наложение отрезков, то-есть отрезки 1-4 4-5 не пересекаются, когда отрезки 1-4 3-5 пересекаются).
Гарантируется, что (ОТВЕТ В ЗАДАЧЕ ВЕРНЫЙ) в отрезках координата начала меньше координаты конца.
Программа должна напечатать одно число – искомое количество отрезков.Входные данные.
Даны два входных файла (файл A и файл B),
каждый из которых содержит в первой строке количество пар N (1 ≤ N ≤ 1e6).
Каждая из следующих N строк содержит два натуральных числа в диапазоне
от -1e9 до 1e9 включительно.
Ввод | Ответ |
---|---|
5 | |
1 4 | |
2 4 | |
5 7 | |
8 9 | |
3 5 | 3 |
Пояснение к примеру
На промежутке 3-4 у нас пересекается аж 3 отрезка: 1-4, 2-4, 3-5
Задача такая же простая, как и на сумму, только теперь вместо суммы мы будем поддерживать максимум.
Попробуйте решить эту задачу самостоятельно.
Код решающий задачу
with open("27.txt", "r") as file: n = int(file.readline()) segments = [list(map(int, file.readline().split())) for _ in range(n)] points = [] for start, end in segments: points += [(start, 1)] # 1 - начало отрезка points += [(end, -1)] # -1 - конец отрезка points.sort() amount_of_segments = 0 # нынешнее количество пересекающихся отрезков max_intersection = 0 # максимальное пересечение for p, t in points: # координата точки и её тип (начало/конец) # максимум среди нынешнего количества пересечения и максимального max_intersection = max(max_intersection, amount_of_segments) amount_of_segments += t # добавляем тип (смотри пояснения) print(max_intersection)
Главная идея:
- Заменить счётчик суммы на максимальное пересечение
Количество отрезков, где пересекаются ровно X
Имеется набор данных, состоящий из отрезков лежащих на прямой (отрезок это пара целых чисел).
Вам требуется найти количество отрезков в исходном наборе, которые имеют пересечения с X и более другими отрезками (под пересечением имеет ввиду наложение отрезков, то-есть отрезки 1-4 4-5 не пересекаются, когда отрезки 1-4 3-5 пересекаются).
Гарантируется, что (ОТВЕТ В ЗАДАЧЕ ВЕРНЫЙ) в отрезках координата начала меньше координаты конца.
Программа должна напечатать одно число – искомое количество отрезков.Входные данные.
Даны два входных файла (файл A и файл B),
каждый из которых содержит в первой строке количество пар N (1 ≤ N ≤ 1e6) и число X (1 ≤ X ≤ N).
Каждая из следующих N строк содержит два натуральных числа в диапазоне
от -1e9 до 1e9 включительно.
Ввод | Ответ |
---|---|
5 2 | |
1 4 | |
2 4 | |
5 7 | |
8 9 | |
3 6 | 3 |
Пояснение к примеру
- Отрезок 1-4 пересекается с отрезками 2-4 и 3-6 (2 ≥ 2), поэтому он входит в наш ответ
- Отрезок 2-4 пересекается с отрезками 1-4 и 3-6 (2 ≥ 2), поэтому он входит в наш ответ
- Отрезок 3-6 пересекается с отрезками 1-4 и 2-4 (2 ≥ 2), поэтому он входит в наш ответ
- Отрезок 5-7 пересекается ТОЛЬКО с 3-6 (1 < 2), поэтому он НЕ входит в наш ответ
- Отрезок 8-9 не пересекается ни с чем (0 < 2), поэтому он НЕ входит в наш ответ
Это достаточно сложная задача. Её основная сложность в том, что если концы двух отрезков одинаковые,
то мы можем потерять какой-то отрезок из ответа (см. пример). Так что этот случай нужно обработать дополнительно или придумать какую-то более интересную идею…
with open("27/ScanLine/input_x_or_more.txt", "r") as file: n, x = map(int, file.readline().split()) segments = [list(map(int, file.readline().split())) for _ in range(n)] points = [] for i, (start, end) in enumerate(segments): # давайте пронумеруем отрезки, чтобы их отличать друг от друга points += [(start, 1, i)] # 1 - начало отрезка points += [(end, -1, i)] # -1 - конец отрезка points.sort() answer = set() # ответ (индексы отрезков которые имеют x или более пересечений) current_segments = set() # нынешние отрезки (которые сейчас открыты) prev_p = 0 for p, t, i in points: # координата точки, её тип (начало/конец) и индекс отрезка if len(current_segments) > x: # если отрезок пересекается с x или более отрезками answer |= current_segments # добавляем в ответ всё наше множество отрезков if t == 1: # добавляем новый отрезок в список нынешних current_segments.add(i) if t == -1: # удаляем отрезок из списка нынешних current_segments.remove(i) print(len(answer))
Главные идеи:
- Мы храним все индексы нынешних отрезков (и ответа) в set’е, чтобы у нас удалялись повторения
Заключение
ScanLine очень крутая идея, которая решает множество задач на отрезки (но, к сожалению, не все). В случае появления
задач на отрезки важно подумать, может ли сканирующая прямая облегчить решение задачи. И если может, перепроверьте ваш алгоритм на дополнительных тестах.
Очень советую разобрать в дополнении к моей методичке, дополнительные ресурсы ниже.
Ресурсы
Если что-то было непонятно, советую изучить дополнительные ресурсы:
- Очень советую эту статью от Tinkoff Generation для укрепления материала
- Расширенная задача для “2D отрезков” на плоскости
- Более простое объяснение алгоритма от ИТМО
- БЕСПЛАТНЫЕ слитые курсы ЛКШ с задачами и разборами, прям советую решить для проверки знаний
- Ещё задачи с тестирующей системой (HARD)
Контакты
- Мой телеграм
- Чат с будущими стобальниками
Отдельное спасибо редактору – Екатерине Максимовой <3
Страницы 1
Чтобы отправить ответ, вы должны войти или зарегистрироваться
#1 11 мая 2005г. 16:29:43
- diz
- Восстановленный участник
- На форуме с 18 октября 2004г.
- Сообщений: 107
- Спасибо: 0
Тема: Как найти общую длину всех выделенных отрезков?
Мне часто приходится чертить раскладки прогонов, у которых различная длина, и что бы узнать общую длину всех прогонов я выделяю каждый, смотрю в свойствах его длину и на калькуляторе складываю. Подскажите пожалуста, может это можно как либо ускорить. Заранее спасибо,
#2 Ответ от Умник 11 мая 2005г. 17:04:36
- Умник
- Восстановленный участник
- На форуме с 17 июня 2004г.
- Сообщений: 206
- Спасибо: 0
Re: Как найти общую длину всех выделенных отрезков?
не помню толи сам накарябал то ли на форуме срисовал используйте этот лисп:
(defun c:ln (/) (apply '+ (mapcar '(lambda (x) (vlax-curve-getDistAtParam (vlax-ename->vla-object x) (vlax-curve-getEndParam x) ) ) (vl-remove-if 'listp (mapcar 'cadr (ssnamex (ssget))) ) ) ) )
#3 Ответ от diz 11 мая 2005г. 17:15:13
- diz
- Восстановленный участник
- На форуме с 18 октября 2004г.
- Сообщений: 107
- Спасибо: 0
Re: Как найти общую длину всех выделенных отрезков?
Большое спасибо. Только если честно, я ещё не профессионал и не знаю куда это писать. Но буду искать. Если не трудно подскажите
#4 Ответ от VK 11 мая 2005г. 19:51:13
- VK
- Восстановленный участник
- На форуме с 8 июня 2002г.
- Сообщений: 224
- Спасибо: 0
Re: Как найти общую длину всех выделенных отрезков?
#5 Ответ от Евгений Елпанов 11 мая 2005г. 21:08:43
- Евгений Елпанов
- Активный участник
- Откуда: Москва
- На форуме с 2 июля 2004г.
- Сообщений: 2,538
- Спасибо: 10
Re: Как найти общую длину всех выделенных отрезков?
> Умник
Програмка отличная, но как-то непонятно:
(vlax-curve-getDistAtParam (vlax-ename->vla-object x);вызов через vla-obj (vlax-curve-getEndParam x);вызов через ent-name )
В хелпе сказано, обе эти функции вызывать через vla-obj
но
(vlax-curve-getDistAtParam x ;вызов через ent-name (vlax-curve-getEndParam x);вызов через ent-name )
Тоже отлично работает…
Может ли кто-нибудь прокаментировать?
#6 Ответ от Евгений Елпанов 11 мая 2005г. 21:43:59
- Евгений Елпанов
- Активный участник
- Откуда: Москва
- На форуме с 2 июля 2004г.
- Сообщений: 2,538
- Спасибо: 10
Re: Как найти общую длину всех выделенных отрезков?
Попробовал запустить все vlax-curve с параметром ent-name
(car (entsel))
Все отработали без ошибок! Хотя хелп напоминает, что надо
(vlax-ename->vla-object (car (entsel)))
Интересно, это недокументированная возможность или…
#7 Ответ от diz 12 мая 2005г. 10:51:49
- diz
- Восстановленный участник
- На форуме с 18 октября 2004г.
- Сообщений: 107
- Спасибо: 0
Re: Как найти общую длину всех выделенных отрезков?
Нашёл на подсказанной вами ссылке програмку
(vl-load-com)
(defun entLen ( / set:entities int:allEntities int:curveEntities int:l rea:length)
(setq set:entities (ssget))
(if set:entities
(progn
(setq int:allEntities (sslength set:entities) ; количество выбранных примитивов
int:curveEntities 0 ; счетчик линейных примитивов
int:l 0 ; счетчик
rea:length 0.0 ; общая длина линейных примитивов
) ;_ setq
(while (< int:l (sslength set:entities))
(if (not
(vl-catch-all-error-p
(vl-catch-all-apply
‘vlax-curve-getStartPoint
(list (vlax-ename->vla-object (ssname set:entities int:l)))
) ;_ vl-catch-all-apply
) ;_ vl-catch-all-error-p
) ;_ not
(setq int:curveEntities (1+ int:curveEntities)
rea:length (+ rea:length
(vlax-curve-getDistAtParam
(vlax-ename->vla-object (ssname set:entities int:l))
(vlax-curve-getEndParam (ssname set:entities int:l))
) ;_ vlax-curve-getDistAtParam
) ;_ +
) ;_ setq
) ;_ if
(setq int:l (1+ int:l))
) ;_ while
(princ (strcat “n Выбрано примитивов: ” (itoa int:allEntities)
“, из них линейных: ” (itoa int:curveEntities)
“n Общая длина линейных примитивов: ” (rtos rea:length)
)
)
) ;_ progn
(alert “Примитивы не выбраны!”)
) ;_ if
(prin1)
) ;_ defun
Создал файл с расширением LSP
Загрузил приложение
После ввода в командную строку (entLen), следует предложение выбора объекта. Но после его выбора, хоть я нажимаю на Enter, хоть на првую кнопку мыши, всё сбрасывается.
Что делать?
P.S. Точно так же загружаю програмку подсказанную Умником, набираю c:ln и полное молчание.
#8 Ответ от Александр Ривилис 12 мая 2005г. 12:03:45
- Александр Ривилис
- Активный участник
- Откуда: Украина / Киев
- На форуме с 15 апреля 2005г.
- Сообщений: 8,661
- Спасибо: 158
Re: Как найти общую длину всех выделенных отрезков?
Какая верисия AutoCAD?
#9 Ответ от diz 12 мая 2005г. 13:18:59
- diz
- Восстановленный участник
- На форуме с 18 октября 2004г.
- Сообщений: 107
- Спасибо: 0
Re: Как найти общую длину всех выделенных отрезков?
2005
#10 Ответ от diz 12 мая 2005г. 13:26:53
- diz
- Восстановленный участник
- На форуме с 18 октября 2004г.
- Сообщений: 107
- Спасибо: 0
Re: Как найти общую длину всех выделенных отрезков?
Кстати, программа
(vl-load-com) (defun entLen ( / set:entities int:allEntities int:curveEntities int:l rea:length) (setq set:entities (ssget)) (if set:entities (progn (setq int:allEntities (sslength set:entities) ; количество выбранных примитивов int:curveEntities 0 ; счетчик линейных примитивов int:l 0 ; счетчик rea:length 0.0 ; общая длина линейных примитивов ) ;_ setq (while (< int:l (sslength set:entities)) (if (not (vl-catch-all-error-p (vl-catch-all-apply 'vlax-curve-getStartPoint (list (vlax-ename->vla-object (ssname set:entities int:l))) ) ;_ vl-catch-all-apply ) ;_ vl-catch-all-error-p ) ;_ not (setq int:curveEntities (1+ int:curveEntities) rea:length (+ rea:length (vlax-curve-getDistAtParam (vlax-ename->vla-object (ssname set:entities int:l)) (vlax-curve-getEndParam (ssname set:entities int:l)) ) ;_ vlax-curve-getDistAtParam ) ;_ + ) ;_ setq ) ;_ if (setq int:l (1+ int:l)) ) ;_ while (princ (strcat "n Выбрано примитивов: " (itoa int:allEntities) ", из них линейных: " (itoa int:curveEntities) "n Общая длина линейных примитивов: " (rtos rea:length) ) ) ) ;_ progn (alert "Примитивы не выбраны!") ) ;_ if (prin1) ) ;_ defun
пошла без проблем. Большое спсибо “kos”-у
#11 Ответ от Владимир Громов 12 мая 2005г. 14:06:30
- Владимир Громов
- Активный участник
- На форуме с 10 июля 2004г.
- Сообщений: 8,349
- Спасибо: 4
Re: Как найти общую длину всех выделенных отрезков?
Может, следует в последней программе заменить слово “примитив” на слово “объект”?
#12 Ответ от Александр Ривилис 12 мая 2005г. 14:28:29
- Александр Ривилис
- Активный участник
- Откуда: Украина / Киев
- На форуме с 15 апреля 2005г.
- Сообщений: 8,661
- Спасибо: 158
Re: Как найти общую длину всех выделенных отрезков?
diz пишет:
P.S. Точно так же загружаю програмку подсказанную Умником, набираю c:ln и полное молчание.
Только сейчас до меня дошло!
В командной строке нужно было набирать ln, а не c:ln
А по поводу версии AutoCAD я спросил, т.к. в вериях до AutoCAD 2000 эти функции не работают.
#13 Ответ от diz 12 мая 2005г. 16:39:53
- diz
- Восстановленный участник
- На форуме с 18 октября 2004г.
- Сообщений: 107
- Спасибо: 0
Re: Как найти общую длину всех выделенных отрезков?
Если набираю ln, то следует предложение выбора объекта. Но после его выбора, хоть я нажимаю на Enter, хоть на првую кнопку мыши, всё сбрасывается. Но всё таки лучше чем раньше
#14 Ответ от Fantomas 12 мая 2005г. 18:49:55
- Fantomas
- Восстановленный участник
- На форуме с 7 декабря 2003г.
- Сообщений: 392
- Спасибо: 0
Re: Как найти общую длину всех выделенных отрезков?
Мой вариант:
(defun c:elen(/ fList firSet entSet filOut entList totLen) (vl-load-com) (setq fList '((-4 . "<OR")(0 . "*LINE") (0 . "CIRCLE")(0 . "ARC") (0 . "ELLIPSE")(-4 . "OR>") (-4 . "<NOT")(0 . "MLINE") (-4 . "NOT>")) filOut 0 ); end setq (if (not (and (setq firSet(ssget "_I") entSet(ssget "_I" fList) ); end setq ); end and ); end not (setq entSet(ssget fList)) (setq filOut(-(sslength firSet)(sslength entset))) ); end if (if entSet (progn (setq entList (mapcar 'vlax-ename->vla-object (vl-remove-if 'listp (mapcar 'cadr(ssnamex entSet)))) totLen (apply '+ (mapcar '(lambda (x) (vlax-curve-getDistAtParam x (vlax-curve-getEndParam x))) entList); end mapcar ); end apply ); end setq (if(/= 0 filOut) (princ(strcat "n" (itoa filout) " were filtered out (unsupported type)")) ); end if (princ(strcat "nTotal entities: "(itoa(length entList)) " Total length: "(rtos totLen)); end strcat ); end princ ); end progn (progn (if(/= 0 filOut) (princ(strcat "n" (itoa filout) " were filtered out (unsupported type)")) (princ "nNothing selected") ); end if ); end progn ); end if (princ) ); end c:elen
#15 Ответ от Геннадий aka PG 12 мая 2005г. 20:13:00
- Геннадий aka PG
- Восстановленный участник
- На форуме с 4 апреля 2002г.
- Сообщений: 1,348
- Спасибо: 0
Re: Как найти общую длину всех выделенных отрезков?
Я поюзал первую функцию Умника– нормально работает для линий и дуг.
#16 Ответ от Евгений Елпанов 12 мая 2005г. 20:24:26
- Евгений Елпанов
- Активный участник
- Откуда: Москва
- На форуме с 2 июля 2004г.
- Сообщений: 2,538
- Спасибо: 10
Re: Как найти общую длину всех выделенных отрезков?
> diz
Вопрос, с которого надо было начинать…
Мне часто приходится чертить раскладки прогонов, у которых различная длина, и что бы узнать общую длину всех прогонов я выделяю каждый, смотрю в свойствах его длину и на калькуляторе складываю.
У какого типа примитива ты смотришь длинну?
Что есть прогон – отрезок, полилиния, сплайн или может быть блок?
#17 Ответ от Александр Ривилис 12 мая 2005г. 21:25:16
- Александр Ривилис
- Активный участник
- Откуда: Украина / Киев
- На форуме с 15 апреля 2005г.
- Сообщений: 8,661
- Спасибо: 158
Re: Как найти общую длину всех выделенных отрезков?
… или может быть твердое тело, мультилиния и т.д…
#18 Ответ от Евгений Елпанов 13 мая 2005г. 10:38:42
- Евгений Елпанов
- Активный участник
- Откуда: Москва
- На форуме с 2 июля 2004г.
- Сообщений: 2,538
- Спасибо: 10
Re: Как найти общую длину всех выделенных отрезков?
> Александр Ривилис
Твердое тело наврятли… У него не посмотришь длинну (или что-то выражающее длинну) в свойствах…
Хотя можно передавать в названии слоя или в названии цвета, но не думаю – тогда проще выделить и посмотреть на слой или цвет…
Скорее всего прогон – примитив не имеющий свойства окончания…
#19 Ответ от diz 13 мая 2005г. 10:39:42
- diz
- Восстановленный участник
- На форуме с 18 октября 2004г.
- Сообщений: 107
- Спасибо: 0
Re: Как найти общую длину всех выделенных отрезков?
Мне в основном нужно находить сумму длин прямых линий, и это хорошо выполнияется программой, которрую выложил kos (я писал об этом), но почему то все остальные предложенные программы не идут. Следует предложение выбора объекта, и после выбора хоть я жму на Enter, хоть на правую кнопку, всё сбрасывается. Просто интересно почему?
И ещё вопрос. Я создал кнопку для работающего лиспа, но после перезагрузки компьютера, что бы она работала нужно нужно каждый раз загружать этот лисп. Нельзя ли зделать так, что бы это происходило автоматически?
#20 Ответ от Умник 13 мая 2005г. 10:57:14
- Умник
- Восстановленный участник
- На форуме с 17 июня 2004г.
- Сообщений: 206
- Спасибо: 0
Re: Как найти общую длину всех выделенных отрезков?
> diz
Я использую запосщенную приблуду на 2004 и на 2005 (англ. версии) все работает чудесно… У вас случаем в командной сторке сколько строчек – если 2 то результата видно не будет – надо увеличить количество строк до 3-х – результат выводиться в командную строку или воспользуйтесь такой прогой:
(defun c:ln (/) (alert (strcat "Total Length is " (rtos (apply '+ (mapcar '(lambda (x) (vlax-curve-getDistAtParam (vlax-ename->vla-object x) (vlax-curve-getEndParam x) ) ) (vl-remove-if 'listp (mapcar 'cadr (ssnamex (ssget))) ) ) ) 2 2 ) ) ) )
кстати говоря, если вы не использовали раньше лиспов то нужно дописать строчку:
(vl-load-com)
второй после defun
у меня подгружено больше двух сотен функций и загружать с каждой функцией (vl-load-com) смысла нет …
#21 Ответ от Александр Ривилис 13 мая 2005г. 11:28:28
- Александр Ривилис
- Активный участник
- Откуда: Украина / Киев
- На форуме с 15 апреля 2005г.
- Сообщений: 8,661
- Спасибо: 158
Re: Как найти общую длину всех выделенных отрезков?
> diz
Чтобы не нужно было загружать вручную lsp-файл, впришите в макрос для кнопки:
^C^C^P(if (null entLen) (load "entlen.lsp")) (entlen) ^P
Подразумевается, что имя lsp-файла entlen.lsp и он находится в путях доступа AutoCAD.
#22 Ответ от diz 13 мая 2005г. 11:37:05
- diz
- Восстановленный участник
- На форуме с 18 октября 2004г.
- Сообщений: 107
- Спасибо: 0
Re: Как найти общую длину всех выделенных отрезков?
У меня действительно была 1 строка в командной строке. Теперь всё получается. Всем большое спасибо!!!
#23 Ответ от Геннадий aka PG 13 мая 2005г. 11:37:05
- Геннадий aka PG
- Восстановленный участник
- На форуме с 4 апреля 2002г.
- Сообщений: 1,348
- Спасибо: 0
Re: Как найти общую длину всех выделенных отрезков?
> diz
> Александр Ривилис
Лучше сделать свое отдельное меню (пусть даже пока из обной кнопки) и подгрузить егою
Подробнее
http://cadhlp.kulichki.com/pdmnu.htm
Посмотри там же сборник CADHLP там есть свое меню и в разделе Расчеты аналогичная прога, взятая тут
/* https://www.caduser.ru/forum/topic11823.html */ (defun C:Dlina (/ Nab Sum i Curve Param) (vl-load-com) (if (setq Nab (ssget)) (progn (setq Sum 0 i 0) (repeat (sslength Nab) (setq Curve (vlax-ename->vla-object (ssname Nab i)) i (1+ i) Param (vl-catch-all-apply 'vlax-curve-getEndParam (list Curve)) ) (if (not (vl-catch-all-error-p Param)) (setq Sum (+ Sum (vlax-curve-getDistAtParam Curve Param))) ) ) ) ) (princ (strcat "nСумма длин выбранных элементов равна: " (rtos Sum 2 2))) (prin1) )
Страницы 1
Чтобы отправить ответ, вы должны войти или зарегистрироваться