46
Динамическое программирование. Примеры задач Федор Царев Спецкурс «Олимпиадное программирование» Лекция 5 13.04.2009 Санкт-Петербург, Гимназия 261

05 динамическое программирование

Embed Size (px)

Citation preview

Page 1: 05 динамическое программирование

Динамическое программирование.

Примеры задач

Федор Царев

Спецкурс «Олимпиадное программирование»

Лекция 5

13.04.2009Санкт-Петербург, Гимназия 261

Page 2: 05 динамическое программирование

2 из 45

Цель лекции

Изучить еще несколько примеров задач, решаемых с помощью динамического программирования, и их решения

Page 3: 05 динамическое программирование

3 из 45

Признаки возможности применения ДП

Возможность разбиения задачи на подзадачи (метод «разделяй-и-властвуй»)

Наличие свойства оптимальности для подзадач – оптимальный ответ для большой задачи строится на основе оптимальных ответов для меньших

Наличие перекрывающихся подзадач

Page 4: 05 динамическое программирование

4 из 45

Этапы решения задачи методом динамического программирования

1. Разбиение задачи на подзадачи

2. Построение рекуррентной формулы для вычисления значения функции (включая начальные условия)

3. Вычисление значения функции для всех подзадач (важен порядок вычисления)

4. Восстановление структуры оптимального ответа

Page 5: 05 динамическое программирование

5 из 45

Наибольшая возрастающая подпоследовательность

Задана последовательность чисел a1, a2, …, an

Необходимо найти возрастающую подпоследовательность наибольшей длины

1 5 3 7 1 4 10 15

Page 6: 05 динамическое программирование

6 из 45

Перебор?

Число различных подпоследовательностей: (2n – 1)

Можно применять для n ≤ 20

1 2 3

1

2

3

1 2

1 3

2 3

1 2 3

Page 7: 05 динамическое программирование

7 из 45

Разбиение на подзадачи

Идея: найти наибольшую возрастающую подпоследовательность среди первых i элементов: a1, a2, …, ai

Попробуйте построить рекуррентную формулу

Более точно: найти наибольшую возрастающую подпоследовательность среди первых i элементов: a1, a2, …, ai, последний элемент в которой - ai

Page 8: 05 динамическое программирование

8 из 45

Рекуррентная формула

d[i] – длина наибольшей возрастающей подпоследовательности, которая заканчивается в ai

(d[j])max1d[i],1 ij aaij

Считается, что максимум равен нулю, если таких индексов j нет

Page 9: 05 динамическое программирование

9 из 45

Начальные условия

Можно сделать немного проще Считаем, что в начало добавлен a0=–∞, а

d[0] = 0 Теперь можно не делать никаких

предположений, так как всегда найдется некоторый индекс j

(d[j])max1d[i],0 ij aaij

Page 10: 05 динамическое программирование

10 из 45

Пример (1)

1 151041735a

0d

Page 11: 05 динамическое программирование

11 из 45

Пример (2)

1 151041735a

0 1d

Page 12: 05 динамическое программирование

12 из 45

Пример (3)

1 151041735a

0 21d

Page 13: 05 динамическое программирование

13 из 45

Пример (4)

1 151041735a

0 221d

Page 14: 05 динамическое программирование

14 из 45

Пример (5)

1 151041735a

0 3221d

Page 15: 05 динамическое программирование

15 из 45

Пример (6)

1 151041735a

0 13221d

Page 16: 05 динамическое программирование

16 из 45

Пример (7)

1 151041735a

0 313221d

Page 17: 05 динамическое программирование

17 из 45

Пример (8)

1 151041735a

0 4313221d

Page 18: 05 динамическое программирование

18 из 45

Пример (9)

1 151041735a

0 4313221d 5

Page 19: 05 динамическое программирование

19 из 45

Программа

d[0] := 0;for i := 1 to n do beginmax := 0;for j := 1 to i – 1 do begin

if (a[j] < a[i]) and (d[j] > max) then begin

max := d[j];end;

end;d[i] := max + 1;

end;

Page 20: 05 динамическое программирование

20 из 45

Восстановление ответа Где находится длина L наибольшей

возрастающей подпоследовательности?

d[i] maxL1 ni

L := 0;

pos := -1;

for i := 1 to n do begin

if (d[i] > max) then begin

max := d[i];

pos := i;

end;

end;

Page 21: 05 динамическое программирование

21 из 45

Вычисление с сохранением информации для восстановления ответа

d[0] := 0;prev[0] := -1;for i := 1 to n do beginmax := 0;bestj := -1;for j := 1 to i – 1 do begin

if (a[j] < a[i]) and (d[j] > max) then begin

max := d[j];bestj := j;

end;end;d[i] := max + 1;prev[i] := bestj;

end;

Page 22: 05 динамическое программирование

22 из 45

Восстановление ответа

procedure restore(i : integer);

begin

if (i > 0) then begin

restore(prev[i]);

write(a[i]);

end;

end;

Page 23: 05 динамическое программирование

23 из 45

Пример

1 41735a

0 313221d

1510

54

-1 303110prev 76

1 3 4 10 15

Page 24: 05 динамическое программирование

24 из 45

Время работы

Время работы этого алгоритма – O(n2) Можно ли быстрее?

Page 25: 05 динамическое программирование

25 из 45

Более быстрый алгоритм

Похоже, что от вычисления d[i] никуда не деться

Попробуем вычислять d[i] быстрее Пусть last[i] – минимальное последнее

число в возрастающей подпоследовательности длины i

Page 26: 05 динамическое программирование

26 из 45

Свойство массива last

Этот массив является неубывающим Действительно, пусть i < j, но last[i] > last[j] Из подпоследовательности длины i можно

сделать подпоследовательность длины j, поэтому last[j] ≤ last[i] (last[j] – минимальный, last[i] – некоторый)

Page 27: 05 динамическое программирование

27 из 45

Вычисление d[i]

Находим место в массиве last, на которое следует поставить a[i] – такую позицию j, что last[j-1] < a[i] ≤ last[j]

Это означает, что максимальная длина подпоследовательности, которая заканчивается в a[i] есть j (d[i] = j)

Позицию j надо искать с помощью двоичного поиска

Время работы алгоритма – O(nlogn)

Page 28: 05 динамическое программирование

28 из 45

Упражнения

Продумать, как сохранять информацию для восстановления ответа

Реализовать этот алгоритм

Page 29: 05 динамическое программирование

29 из 45

Задача о рюкзаке

Есть рюкзак вместимостью W и n предметов, каждый из которых характеризуется ценностью pi и весом wi

Необходимо выбрать несколько предметов так, чтобы их суммарная ценность была максимальна, а суммарный вес не превышал W

Page 30: 05 динамическое программирование

30 из 45

Разбиение на подзадачи

Два параметра – число обработанных предметов и вместимость рюкзака

c[i][j] – максимальная суммарная стоимость, которую можно набрать первыми i предметами так, чтобы их вес не превосходил j

Page 31: 05 динамическое программирование

31 из 45

Рекуррентная формула

)p]w-1][j-c[i1][j],-max(c[i c[i][j] ii

Очередной предмет можно либо взять, либо не взять

Page 32: 05 динамическое программирование

32 из 45

Начальные условия

c[0][j] = 0 для j=0…W c[i][0] = 0 для i=0…n

0 0000

0

0

0

0

0

Page 33: 05 динамическое программирование

33 из 45

Два способа реализации

Метод заполнения таблицы можно реализовать двумя способами «Динамика назад» (этот метод использовался

во всех рассмотренных задачах) «Динамика вперед»

(i, j)

(i-1, j-w[i])

(i-1, j)

(i, j)

(i+1, j)

(i+1, j+w[i+1])

Page 34: 05 динамическое программирование

34 из 45

«Динамика вперед»for i := 0 to n do begin

for j := 0 to W do beginc[i][j] := -INF;

end;end;for i := 0 to n – 1 do begin

for j := 0 to W do beginif (c[i][j] = -INF) then continue;c[i+1][j]:=max(c[i][j], c[i+1][j]);if (j + w[i + 1] <= W) then begin c[i + 1][j + w[i + 1]] = max(c[i][j] + p[i+1],

c[i + 1][j + w[i + 1]]);end;

end;end;

Не осуществляются переходыиз недостижимых состояний

Page 35: 05 динамическое программирование

35 из 45

Восстановление ответа

Необходимо запоминать для каждого состояния (i, j) надо ли брать очередной предмет

Реализуйте сами!

Page 36: 05 динамическое программирование

36 из 45

Время работы алгоритма

Время работы этого алгоритма – O(nW) Таким образом, он применим только для

относительно небольших значений весов предметов

Page 37: 05 динамическое программирование

37 из 45

Упражнения

Решите задачу о рюкзаке для случая, когда имеется неограниченное число предметов каждого типа

Решите задачу о рюкзаке для случая, когда предметы можно брать не полностью (не золотые слитки, а золотой песок)

Решите смешанную задачу о рюкзаке – часть предметов можно брать только полностью, а остальные – можно и не полностью

Page 38: 05 динамическое программирование

38 из 45

Оптимальная триангуляция многоугольника

Задан выпуклый многоугольник

Необходимо разбить его на треугольники, проведя несколько диагоналей

Суммарный периметр треугольников должен быть как можно меньшим

Кстати, сколько придется провести диагоналей, если в многоугольнике N углов?

Page 39: 05 динамическое программирование

39 из 45

Нумерация вершин многоугольника

Вершины (n+1)-угольника нумеруются числами от 0 до n

При этом когда говорится о вершине «номер k» имеется в виду вершина «номер k mod n» (то есть vn=v0, …)

v0

v1

v2

v3

v4

v5

Page 40: 05 динамическое программирование

40 из 45

Разбиение на подзадачи

После вырезания одного треугольника, многоугольника распадается на два, которые можно рассматривать отдельно

v0

v1

v2

v3

v4

v5

v0

v1

v2

v3

v3

v4

v5

Page 41: 05 динамическое программирование

41 из 45

Строение оптимального решения

Рассмотрим оптимальную триангуляцию заданного (n+1)-угольника v0,v1, …, vn

Ребро v0vn входит в некоторый треугольник Пусть это треугольник v0vnvk Тогда стоимость триангуляции равна

Стоимость этого треугольника + Стоимость триангуляции v0, v1, …, vk +

Стоимость триангуляции vk, vk+1, …, vn

Page 42: 05 динамическое программирование

42 из 45

Рекуррентная формула

d[i][j] – минимальная стоимость триангуляции многоугольника vi-1…vj (1≤i<j≤n)

Ответ находится в d[1][n]

))vvp(v1][j]d[k(d[i][k]mind[i][j] jk1-ijki

v0

v1

v2

v3

v4

v5

Начальные условия:

•d[i][i] = 0

•d[i][j] = -∞ при i > j

Page 43: 05 динамическое программирование

43 из 45

Восстановление ответа

Для каждой подзадачи необходимо запомнить оптимальное значение числа k

Реализуйте самостоятельно!

Page 44: 05 динамическое программирование

44 из 45

Упражнения

Пусть стоимостью треугольника считается его площадь. Как найти оптимальную триангуляцию?

Пусть необходимо минимизировать суммарную длину проведенных диагоналей. Как найти оптимальную триангуляцию в этом случае?

Page 45: 05 динамическое программирование

45 из 45

Выводы Рассмотрены три примера задач,

решаемых методом динамического программирования

Метод заполнения таблицы может быть реализован двумя способами – «динамика вперед» и «динамика назад»

Необходимо следить за тем, чтобы не выполнялись переходы из недостижимых состояний

Page 46: 05 динамическое программирование

Спасибо за внимание!

Вопросы? Комментарии?

[email protected]