5.8.5 Задача триангуляции многоугольника
Рисунок 5.17 – Иллюстрация к задаче триангуляции
Имеется выпуклый n-угольник Рn = (v1, v2...vn). Надо разбить его на непересекающиеся треугольники так, чтобы сумма длин отрезков разбиения была наименьшей. На рисунке показано два возможных ва- рианта триангуляции шестиуголь- ника. Будем решать данную задачу в соответствии с вышеизложенными пунктами.
Договоримся обозначать символом Рi..j = (vi...vj) многоугольник, обра- зованный вершинами от vi до vj (ij), а символом треугольник с вершинами vk, vl, vm. Таким образом, Р1..n это исходный много- угольник. Пусть d(vi, vj) равняется длине отрезка между вершинами vi и vj), если vivj диагональ много- угольника и d(vi, vj) = 0, если vivj его сторона. Пусть f(i..j) сумма длин отрезков оптимального раз- биения многоугольника Рi..j на тре- угольники. Заметим следующее: из условия непересекаемости треу- гольников следует, что каждое реб- ро Р1..n входит ровно в один из тре- угольников. В частности это каса- ется и ребра v1vn которое с некоей вершиной k (1 < k < n) образует . Тогда рассмотрим два ос- тавшиеся многоугольника Р1..k и Рk..n, которые в нашем случае и бу- дут ни чем иным, как подзадачами. Если для всех таких подзадач (при разных k) уже найдено оптималь- ное решение, то остается лишь вы- брать оптимальное значение k.
В задаче требуется найти f(1..n). Построим формулу для нахожде- ния f(i..j) суммы длин отрезков триангуляции для многоугольника Рi..j. Имеет место следующая зави- симость:
Формула следует из того, что вы- брав вершину k, мы получаем два меньших многоугольника Рi..k и Рk..j, которые надо триангулировать далее и два отрезка (vivk и vkvj), длины которых входят в нашу три- ангуляцию (если один из них сто- рона многоугольника, то согласно выбору d его длина не будет учиты- ваться). Очевидно, что эта форму- ла соответствует рекурсивной про- цедуре с циклом по k внутри и входными параметрами i и j. Чем не принцип «разделяй и власт- вуй»? Почти, но не совсем.
Рисунок 5.18 – Разбиение на подзадачи при триангуляции
Покажем с помощью дерева, как будем собирать целостное реше- ние из решений подзадач для се- миугольника. Задача найти f(1..7) распалась на 5 пар задач (f(1..k), f(k..7)) для k=2..5. Далее каждая из подзадач рекурсивно разбивается на более мелкие, пока последние не ста- нут тривиальными (вида f(i..i+1) или f(i..i). Если присмотреться к этому дереву, видно, что одни и те же подзадачи на разных вет- ках дерева повторяются. Более того, повторяются не только три- виальные подзадачи (например, f(2..3) на голубых узлах), но и не- тривиальные (задача f(4..7) на зе- леных узлах), которые порожда- ют целые деревья рекурсивных вызовов.
Динамическое программирование заключается как раз в том, что под- задачи нужно решать только один раз, а при каждом последующем вызове рекурсивной процедуры для решения той же подзадачи нужно просто прочитать данные из таблицы. Так мы и поступим при написании кода.
const
InFileName = 'in.txt'; МахN = 50; NotCalculated = -1; MaxReal = 1e30;
var
N: integer; (количество вершин) d: array[1..MaxN, 1..MaxN] of real;
(расстояния между вершинами)
f: array[1..MaxN, 1..MaxN] of real;
(динамическая таблица решений)
Cut: аггау[1..иахМ, 1..MaxN] of integer;
(таблица для запоминания вершины разреза)
(функция нахождения расстояния между двумя точками}
function distance(x1,у1,х2,у2: real):real;
begin
distance := sqrt(sqr(xl-x2)+sqr(у1-у2));
end;
(процедура считывания входящих данных и нахождения расстояний
между вершинами)
procedure ReadData; var
ft: text; х,у: array[1..MaxN] of real;
i,j: integer;
begin
assign(ft,InFileName); reset(ft); read(ft,N); for i:= 1 to N do
readln(ft, x[1],у[i]);
close(ft);
for i:= 1 to N do
for j:= 1 to д do
begin
if abs(i-j) <= 1 then
(если это сторона, то) d[i,j]:=0 (в разрез она не войдет,
значит пусть ее длина=0)
еlsе d[i,j]:=Distance(x[i],у[1],х[2],у[2]);
f [i,j]:= NotCalculated;
(ставим отметку, что подзадача f(i,.j) не peшeнa)
end;
end;
(рекурсивная процедура поиска оптимального решения)
procedure Divide(i,j:integer); var k, minK: integer;
L, minL: real;
begin
if f[i,j] <> NotCalculated then
(если подзадача уже решалась, выходим) exit;
if i+2 >= j then begin
(если задача тривиальна (треугольник, отрезок или точка))
Cut[i,j] := 0; (то разбиений нет)
f [1,2] := 0; (и длина такого несуществующего разбиения
равна нулю)
exit;
end;
minL := MaxReal;
(ищем минимум no k (рекуррентная формула))
for k := 1<1 to j-1 do begin
Divide(i,k); (решаем подзадачу f(i..k))
Divide(k,j); (решаем подзадачу f(k..2})
L := P[i,k] + F[k,j] + D[i,k] + D[k,j];
if L < MinL then begin
MinL := L; (запоминаем минимальную длину}
MinK := k; (и точку разбиения)
end;
end;
F[i,j] MinL; (запоминаем оптимум в динамической таблице)
Cut[i,j] :=MinK;
end;
(процедура рекурсивно выводит разбиение)
procedure PrintCut(i,j integer) begin
if Cut[i,j] <> 0 then begin
if i+1 < Cut[i,j] then
WriteLn(i,'-',Cut[i,j]);
if Cut[i,j]+l < j then
WriteLn(Cut[i,j],'-',3);
PrintCut(i,Cut[1 3));
PrintCut(Cut[i,j],j);
end;
end;
begin
ReadData; (читаем входные данные) Divide(1,N); (выполняем разбиение) (выводим результаты) wreteln('Min(mal cut length:f[l,n]);
writeLn('Cut the polygon along these diagonals:');
PrintCut(1,N);
end.
Листинг 5.27 – ДП-алгоритм триангуляции многоугольника
Оптимальное решение будет нахо- диться в нашей таблице после вы- зова рекурсивной процедуры с па- раметрами (1, n).
- Министерство образования Российской Федерации
- Содержание
- 1.2 Скорость роста функций
- 1.3 Анализ алгоритмов; время работы в лучшем, худшем случаях и в среднем
- 1.4 Типы данных, структуры данных и абстрактные типы данных
- 1.5 Динамические множества
- 2 Алгоритмы сортировок
- 2.1 Понятие внутренней и внешней сортировки
- 2.2 Сортировка вставками
- 2.3 Сортировка слиянием
- 2.3.1 Описание алгоритма
- 2.3.2 Анализ времени работы алгоритмов «разделяй и властвуй»
- 2.3.2 Анализ времени работы сортировки слиянием через рекуррентное соотношение
- 2.3.3 Анализ времени работы сортировки слиянием через геометрическую интерпретацию
- 2.4 Пирамидальная сортировка
- 2.4.1 Введение в алгоритм
- 2.4.2 Сохранение основного свойства кучи
- 2.4.3 Построение кучи
- 2.5 Быстрая сортировка
- 2.5.1 Введение в алгоритм
- 2.5.2 Описание
- 2.5.3 Разбиение массива
- 2.5.4 Особенности работы быстрой сортировки
- 2.6 Особенности реализации алгоритмов сортировки; сортировка за линейное время
- 2.6.1 Введение
- 2.6.2 Разрешающее дерево сортировки сравнениями
- 2.7 Цифровая сортировка
- 2.8 Сортировка вычерпыванием
- 2.8.1 Описание алгоритма
- 2.8.2 Вероятностный анализ времени работы сортировки вычерпыванием
- 2.8.3 Анализ времени работы сортировки вычерпыванием через геометрическую интерпретацию
- 2.9 Сортировка подсчетом
- 2.9.1 Описание алгоритма
- 2.9.2 Анализ времени работы
- 3 Элементарные и нелинейные структуры данных
- 3.1 Элементарные структуры: список, стек, очередь, дек
- 3.1.1 Список Линейный однонаправленный список
- Линейный двунаправленный список
- Двунаправленный список с фиктивными элементами
- Циклические списки
- Циклический однонаправленный список
- Циклический двунаправленный список
- 3.1.2 Стек
- 3.1.3 Очередь
- 3.1.3 Дек
- 3.2 Нелинейные структуры данных
- 3.2.1 Представление корневых деревьев в эвм
- Обходы деревьев
- 3.2.2 Двоичные деревья Спецификация двоичных деревьев
- Реализация
- Обходы двоичных деревьев
- 3.2.3 Двоичные деревья поиска Основные операции
- Минимум и максимум
- Следующий и предыдущий элементы
- Добавление и удаление
- Случайные деревья поиска
- Оптимальные деревья поиска
- 4 Хеширование
- 4.1 Введение
- 4.2 Прямая адресация; таблицы с прямой адресацией
- 4.3 Хеш – таблицы; возникновение коллизий и их разрешение
- Разрешение коллизий с помощью цепочек
- Анализ хеширования с цепочками
- 4.4 Способы построения хеш – функций Выбор хорошей хеш-функции
- Ключи как натуральные числа
- Деление с остатком
- Умножение
- Универсальное хеширование
- 4.5 Открытая адресация; способы вычисления последовательности испробованных мест: линейная последовательность проб, квадратичная последовательность проб, двойное хеширование
- Линейная последовательность проб
- 1 / (1 – )
- 5 Основные принципы разработки алгоритмов
- 5.1 Введение в теорию графов
- 5.1.1 Графы
- 5.1.2 Представление графов
- 5.2 Алгоритмы на графах: поиск в ширину, поиск в глубину
- 5.2.1 Поиск в ширину (волновой алгоритм)
- 5.2.2 Анализ поиска в ширину
- 5.2.3 Деревья поиска в ширину
- 5.2.4 Поиск в глубину
- 5.2.5 Анализ поиска в глубину
- 5.2.6 Свойства поиска в глубину
- 5.2.7 Классификация рёбер
- 5.3 Топологическая сортировка, задача о разбиении графа на сильно связанные компоненты
- 5.3.1 Топологическая сортировка
- 5.3.2 Сильно связные компоненты
- 5.4 Алгоритм построения минимального остовного дерева
- 5.4.1 Остовные деревья минимальной стоимости
- 5.4.2 Построение минимального покрывающего дерева
- 5.4.3 Алгоритмы Крускала и Пpимa
- 5.4.4 Алгоритм Крускала
- 5.4.5 Алгоритм Прима
- 5.5 Задача нахождения кратчайших путей на графах; алгоритм Флойда; алгоритм Дейкстры
- 5.5.1 Нахождение кратчайшего пути
- 5.5.2 Алгоритм Дейкстры
- 5.5.3 Алгоритм Флойда
- 5.6 Поиск с возвратом
- 5.6.1 Введение
- 5.6.2 Переборные алгоритмы
- 5.6.3 Метод ветвей и границ
- 5.6.4 Метод альфа-бета отсечения
- 5.6.5 Локальные и глобальные оптимальные решения
- 5.7 Метод декомпозиции ( «Разделяй и властвуй»)
- 5.7.1 «Ханойские башни»
- 5.8 Жадные алгоритмы и динамическое программирование
- 5.8.1 Задача о выборе заявок
- 5.8.2 Дискретная задача о рюкзаке
- 5.8.3 Непрерывная задача о рюкзаке
- 5.8.4 Числа Фибоначчи
- 5.8.5 Задача триангуляции многоугольника
- 5.8.6 Дп, жадный алгоритм или что-то другое?