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

advertisement
Динамическое
программирование
Способ решения задач путем
разбиения их на подзадачи.
Числа Фибоначчи





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]
Спасибо за внимание
Задавайте вопросы.
Download