5.8.2 Дискретная задача о рюкзаке
На складе есть N предметов, для оторых известны их веса w[1..N] и их стоимости v[1..М]. На склад про- брался вор, который хочет украсть предметов на максимальную сум- му денег. Однако вес, который вор может вынести, ограничен и рав- няется TotalW. Какие предметы должен взять вор, чтобы их сум- марная стоимость была наиболь- шей, а вес был ограничен величи- ной TotalW ?
Эта задача встречается во многих источниках, но часто ее решают простым backtracking'ом, где на каждом шаге пробуют взять или не взять предмет. Однако такой под- ход не всегда эффективен. Суще- ствует как жадный, так и ДП-алго- ритм ее решения.
Жадный алгоритм поступает так: вычисляется цена единицы веса каждого предмета, то есть рriсе[i] := v[i] / w[i]. Потом предметы сорти- руются в порядке убывания price[i], и вор начинает помещать в свой рюкзак предметы по порядку (i=1,2,...N) из отсортированного списка. Если предмет i не помещается (по ограничению оставшегося свобод- ного веса в рюкзаке) вор рассма тривает следующий предмет 1+1 (price[i + 1] < price[i]), и так до конца. Жадность состоит в том, что вор на каждом шаге пытается взять предмет с наибольшей ценой.
Оценим сложность описанного алгоритма. Для сорти- ровки надо O(N log(N)) операций. Далее надо пробежать циклом по i от 1 до N и пробовать впихнуть предмет i, таким образом это зай- мет еще O(n) операций. Итого, об- щая сложность есть O(N log(N) + N), что в общем случае эквива- лентно O(N log(N)).
Но если хорошо присмотреться, то такой алгоритм не всегда дает оп- тимум. Вот вам пример: 3 предме- та, 1 ($60, 10 кг), 2 ($100, 20 кг), 3 ($120, 30 кг) с ценами соот- ветственно 6 $/кг, 5 $/кг, 4 $/кг. Предметы уже отсортированы по убыванию цены. Допустим, макси- мальный вес рюкзака вора 50 кг. Следуя жадному алгоритму, вор берет первый предмет с ценой 6 $/кг, который весит 10 кг, затем следующий предмет с ценой 5 $/кг (и весом 20 кг), после чего в его рюкзаке остается место только на 20 кг, и оставшийся предмет уже не влезает, так как весит 30 кг. Итого, действуя по жадному алго- ритму, вор украл два предмета на сумму $160 и весом 30 кг. Если бы вор украл второй и третий предме- ты (суммарным весом 50 кг), он бы вынес $220. Как видите, жадный алгоритм не дает оптимального решения, а значит, он является приблизительным алгоритмом.
Если решать эту же задачу с по- мощью динамического програм- мирования, то надо поступить следующим образом. Допустим, надо найти максимальную сумму val[W, i], которую может вынести вор, если допустить, что объем его рюкзака не больше W и мож- но брать предметы от 1 до i (предметы не отсортированы!) Допустим, мы уже нашли все val[1..W, 1..i1] (для веса не боль- ше W и с возможностью брать предметы от 1 до i1). Рассмат- ривается предмет i. Если его вес w[i] меньше W, рассмотрим, сто- ит ли его брать:
Если его взять, то доступный вес в рюкзаке станет Ww[i] и мы сможем забрать предметов на сумму val[W, i] = val[Ww[i], i1] + v[i] (задача val[Ww[i], i1] уже решена, плюс стоимость v[i] это- го предмета);
Если его не брать, то доступный вес остается тем же, и тогда val[W, i] = val[W, i 1].
Из этих двух вариантов выбирается тот, что дает большее значение val[W, i]. Реализация ДП-алгорит- ма будет выглядеть так:
const MAXW = 500; MAXN = 25;
var
val: array[0..MAXW,0..MAXN]of integer; (динамические массивы}
take: array[0.. MAXW,0..MAXN] of boolean;
v,w: array[1..MAXN] of integer;(ценности и веса предметов)
л, TotalW: integer; (кол. предметов,максимальный вес)
weight, i: integer;(переменные циклов}
begin
{читаем входные данные} read(n, TotalW);
for i:=1 to n do read(w(i] v[i]);
(делаем начальную инициализацию для веса 0}
for i:=0 tо n dо begin
val[0,i]:= 0;
take[0,i]:= false;
end;
for weight:=1 to Totalw do val[weight 0] := 0;
for weight:=1 to TotalW do
for i := 1 to N do
if (w[i]>weight)
{если вещь не влезет} {или лучше ее не брать} or ( val[weight, i-1] >= val [weight-w[i],i-l] i v[i))
then begin (то не берем ее)
val[weight,i] :=val[weight,i-1]; take[weight,i] := false;
(отмечаем, что вещь i не взята)
end
else {иначе) begin
{оптимум := оптимум для веса weight-w[i] и использования
вещей 1..i-l + цена вещи i, которую мы берем}
val [weight, i]:= val(weight-w[i],i-1] + v[i];
take[weight,i]:= true;
(отмечаем, что вещь i взята)
end;
(вывод результатов) writeln('Best value:’, val[TotalW, N]);
write('Taken things; '); weight := TotalW; for i := N downto 1 do
if take[weight, i] then begin
write(i,' ');
weight := weight - w[i];
end;
end.
Листинг 5.23 – Дискретная задача о рюкзаке, решаемая ДП-алгоритмом
Этот алгоритм будет выдавать оптимальное решение, но какой ценой? Ценой лишней памяти: надо порядка O(TotalW N) памяти под динамические таблицы стои- мости и запоминания того, брать и каждый предмет или нет. Сложность алгоритма та же: O (TotalW N), что следует из цик- лов по weight и i.
Таким образом, ДП-алгоритм хоть и работает безупречно правильно, но требует дополнительных затрат. Есть, правда, еще одна проблема с ДП-алгоритмом: если TotalW слиш- ком велико, или же оно является действительным (а не целым) чис- лом, то ДП-алгоритм вообще не- применим.
- Министерство образования Российской Федерации
- Содержание
- 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 Дп, жадный алгоритм или что-то другое?