Динамическое программирование Способ решения задач путем разбиения их на подзадачи. Числа Фибоначчи 1, 1, 2, 3, 5, 8, 13, 21, 34, 55.. F(1) = 1 F(2) = 1 … F(n) = F(n – 1) + F(n – 2) Рекуррентное вычисление чисел Фибоначчи int fib(int n) { if (n < 3) return 1; return fib(n - 1) + fib(n - 2); } Минус такого способа Числа Фибоначчи нигде не хранятся и поэтому каждое число будет вычисляться несколько раз. Динамическое вычисление числа Фибоначчи Будем вычислять и хранить числа Фибоначчи от 1 .. n, тогда каждое число нам нужно будет вычислять только один раз. int fib[maxN]; fib[1] = 1; fib[2] = 1; for (int i = 3; i <= n; i++) { fib[i] = fib[i - 1] + fib[i - 2]; } Задача о кузнечике, прыгающем по столбикам Формулировка: Кузнечик находится на нулевом столбике, какое количество способов у кузнечика добраться до столбика с индексом n, если он может прыгать либо на 1 столбик вперед, либо на 2 столбика вперед. Код int ans[maxN] = {0}; ans[0] = 1; ans[1] = 1; for (int i = 2; i <= n; i++) { ans[i] = ans[i - 1] + ans[i - 2]; } Усложним задачу: теперь кузнечик может прыгать еще на 3 столбика вперед. Усложним задачу: теперь кузнечик может прыгать еще на 3 столбика вперед. int ans[maxN] = {0}; ans[0] = 1; ans[1] = 1; ans[2] = 2; for (int i = 3; i <= n; i++) { ans[i] = ans[i - 1] + ans[i - 2] + ans[i - 3]; } Последовательность решения задачи (на примере задачи о кузнечике) 1) Что мы вычисляем? a[i] – количество способов допрыгать до i-ого столбца 2) Какое рекуррентное соотношение? a[i] = a[i – 1] + a[i – 2] + a[i – 3] 3) Какие начальные значения? a[0] = 1; a[1] = 1; a[2] = 2; 4) В каком порядке вычислять значения? Обращаемся к предыдущим 5) Где находится ответ? a[n] Усложним задачу про кузнечика: он может прыгать от 1 до k столбцов вперед Усложним задачу про кузнечика: он может прыгать от 1 до k столбцов вперед int ans[maxN] = {0}; ans[0] = 1; for (int i = 1; i <= n; i++) { for (int j = 1; j <= min(i, k); j++) ans[i] += ans[i - j]; } Усложним задачу: теперь на каждом столбике кузнечик может получить или потерять d[i] монет, определить, сколько максимум монет кузнечик может собрать, дойдя до n-ого столбика. 1) Что мы вычисляем? 1) Что мы вычисляем? a[i] – максимальное количество монет, которое кузнечик может собрать, допрыгав до i-ого столбика 2) Какое рекуррентное соотношение? 1) Что мы вычисляем? a[i] – максимальное количество монет, которое кузнечик может собрать, допрыгав до i-ого столбика 2) Какое рекуррентное соотношение? a[i] = максимальному из k предыдущих столбиков + цена i-ого столбика 3) Какие начальные значения? 1) Что мы вычисляем? a[i] – максимальное количество монет, которое кузнечик может собрать, допрыгав до iого столбика 2) Какое рекуррентное соотношение? a[i] = максимальному из k предыдущих столбиков + цена i-ого столбика 3) Какие начальные значения? a[0] = 0 4) В каком порядке вычислять значения? 1) Что мы вычисляем? a[i] – максимальное количество монет, которое кузнечик может собрать, допрыгав до iого столбика 2) Какое рекуррентное соотношение? a[i] = максимальному из k предыдущих столбиков + цена i-ого столбика 3) Какие начальные значения? a[0] = 0 4) В каком порядке вычислять значения? Обращаемся к предыдущим 5) Где находится ответ? 1) Что мы вычисляем? a[i] – максимальное количество монет, которое кузнечик может собрать, допрыгав до iого столбика 2) Какое рекуррентное соотношение? a[i] = максимальному из k предыдущих столбиков + цена i-ого столбика 3) Какие начальные значения? a[0] = 0 4) В каком порядке вычислять значения? Обращаемся к предыдущим 5) Где находится ответ? a[n] Код int ans[maxN] = {-1e9}; ans[0] = 0; for (int i = 1; i <= n; i++) { for (int j = 1; j <= min(i, k); j++) if (ans[i] < ans[i – j ]) ans[i] = ans[i – j]; ans[i] += d[i]; } Восстановление ответа: Каким путем должен двигаться кузнечик, чтобы собрать максимальную сумму? Восстановление ответа: Каким путем должен двигаться кузнечик, чтобы собрать максимальную сумму? Для каждого i будем сохранять помимо максимального значения еще и столбик, с которого он должен прийти. Код int ans[maxN] = {-1e9}; int from[maxN] = {0}; ans[0] = 0; for (int i = 1; i <= n; i++) { for (int j = 1; j <= min(i, k); j++) if (ans[i] < ans[i – j]) { ans[i] = ans[i – j]; from[i] = i - j; } ans[i] += d[i]; } Само восстановление ответа stack<int> path; while (i != 0) { path.push(i); i = from[i]; } while (!path.empty()) { cout << path.top() << " "; path.pop(); } Задача Черепашка. Черепашка живет в прямоугольной матрице (m x n). Изначально она находится в клетке (1, 1). Черепашка умеет ходить только вниз и вправо. Сколько способов черепашке добраться в клетку (m, n). 1) Что мы вычисляем? 1) Что мы вычисляем? a[i][j] – количество способов добраться до клетки (i, j). 2) Какое рекуррентное соотношение? 1) Что мы вычисляем? a[i][j] – количество способов добраться до клетки (i, j). 2) Какое рекуррентное соотношение? a[i][j] = a[i – 1][j] + a[i][j – 1]; 3) Какие начальные значения? 1) Что мы вычисляем? a[i][j] – количество способов добраться до клетки (i, j). 2) Какое рекуррентное соотношение? a[i][j] = a[i – 1][j] + a[i][j – 1]; 3) Какие начальные значения? a[1][1] = 1; 4) В каком порядке вычислять значения? 1) Что мы вычисляем? a[i][j] – количество способов добраться до клетки (i, j). 2) Какое рекуррентное соотношение? a[i][j] = a[i – 1][j] + a[i][j – 1]; 3) Какие начальные значения? a[1][1] = 1; 4) В каком порядке вычислять значения? Идти либо по строкам, либо по столбцам 5) Где находится ответ? 1) Что мы вычисляем? a[i][j] – количество способов добраться до клетки (i, j). 2) Какое рекуррентное соотношение? a[i][j] = a[i – 1][j] + a[i][j – 1]; 3) Какие начальные значения? a[1][1] = 1; 4) В каком порядке вычислять значения? Идти либо по строкам, либо по столбцам 5) Где находится ответ? a[m][n] Задача о рюкзаке: Есть N предметов, обладающих весом и стоимостью. В рюкзак влезают предметы, суммарный вес которых не превосходит W. Какую максимальную ценность может иметь рюкзак? 1) Что мы вычисляем? 1) Что мы вычисляем? a[i][j] – максимальная стоимость, которую мы можем собрать, взяв только какие-то предметы из первых i, весом не больше j. 2) Какое рекуррентное соотношение? 1) Что мы вычисляем? a[i][j] – максимальная стоимость, которую мы можем собрать, взяв только какие-то предметы из первых i, весом не больше j. 2) Какое рекуррентное соотношение? a[i][j] = max(a[i – 1][j], a[i – 1][j – w[i]] + p[i]) 3) Какие начальные значения? 1) Что мы вычисляем? a[i][j] – максимальная стоимость, которую мы можем собрать, взяв только какие-то предметы из первых i, весом не больше j. 2) Какое рекуррентное соотношение? a[i][j] = max(a[i – 1][j], a[i – 1][j – w[i]] + p[i]) 3) Какие начальные значения? a[0][j] = 0; a[i][0] = 0; 4) В каком порядке вычислять значения? 1) Что мы вычисляем? a[i][j] – максимальная стоимость, которую мы можем собрать, взяв только какие-то предметы из первых i, весом не больше j. 2) Какое рекуррентное соотношение? a[i][j] = max(a[i – 1][j], a[i – 1][j – w[i]] + p[i]) 3) Какие начальные значения? a[0][j] = 0; a[i][0] = 0; 4) В каком порядке вычислять значения? Идти либо по строкам, либо по столбцам 5) Где находится ответ? 1) Что мы вычисляем? a[i][j] – максимальная стоимость, которую мы можем собрать, взяв только какие-то предметы из первых i, весом не больше j. 2) Какое рекуррентное соотношение? a[i][j] = max(a[i – 1][j], a[i – 1][j – w[i]] + p[i]) 3) Какие начальные значения? a[0][j] = 0; a[i][0] = 0; 4) В каком порядке вычислять значения? Идти либо по строкам, либо по столбцам 5) Где находится ответ? a[N][W] Спасибо за внимание Задавайте вопросы.