Любой псевдополиномиальный алгоритм для ограниченного 0-1 мультиранпака?
Предположим, что существует n элементов, например, i1, i2,.... in, каждый из которых имеет известный ограниченный вес w1, w2,... wn. Есть также набор из рюкзаков, например, k1, k2 и km. Рюкзаки однородны, так как все они имеют одинаковую вместимость W. Функция F может определять счет каждого рюкзака. Входами F являются предметы в каждом рюкзаке. Так,
Score of each knapsack i = F(Items in knapsack i)
Теперь я хочу положить НЕКОТОРЫЕ предметы в рюкзаки так, чтобы:
- Вес предметов в рюкзаке не превышает его вместимости W.
- Сумма баллов за все рюкзаки должна быть максимальной
Есть ли решение этой проблемы за полиномиальное время или нет?
Примечание: проблема 0-1, то есть каждый элемент может быть выбран или нет. Все параметры задачи ограничены.
Редактировать 1: Разве нельзя свести эту проблему к упаковке бина, а затем сделать вывод, что это трудная задача NP?
Редактировать 2 В этой задаче каждый элемент имеет три атрибута, например атрибуты ai, bi и ci. Функция F - это линейная функция, которая получает атрибуты элементов внутри нее и выдает результат.
Редактировать 3: Кажется, что эта статья предложила точное решение проблемы с несколькими ранцами. Можно ли это использовать в моем случае?
1 ответ
Как насчет этого?
Учитывая стандартное динамическое решение в Haskell для задачи о ранце 0-1, найденное здесь,
inv = [("map",9,150), ("compass",13,35), ("water",153,200), ("sandwich",50,160),
("glucose",15,60), ("tin",68,45), ("banana",27,60), ("apple",39,40),
("cheese",23,30), ("beer",52,10), ("cream",11,70), ("camera",32,30),
("tshirt",24,15), ("trousers",48,10), ("umbrella",73,40),
("trousers",42,70), ("overclothes",43,75), ("notecase",22,80),
("sunglasses",7,20), ("towel",18,12), ("socks",4,50), ("book",30,10)]
knapsack = foldr addItem (repeat (0,[])) where
addItem (name,w,v) list = left ++ zipWith max right newlist where
newlist = map (\(val, names)->(val + v, name:names)) list
(left,right) = splitAt w list
main = print $ (knapsack inv) !! 400
мы добавляем механизм заполнения, помещая перестановки инвентаря последовательно в следующий рюкзак, в котором есть место,
stuff (name,w,v) left (v2,[]) = (v2,left)
stuff (name,w,v) left (v2,(cap, lst):xs) =
if w <= cap
then (v + v2, left ++ [(cap - w, (name,w,v):lst)] ++ xs)
else stuff (name,w,v) (left ++ [(cap,lst)]) (v2,xs)
и заменить его на отображенную функцию. Собираем все вместе:
inv = [("map",9,150), ("compass",13,35), ("water",153,200), ("sandwich",50,160),
("glucose",15,60), ("tin",68,45), ("banana",27,60), ("apple",39,40),
("cheese",23,30), ("beer",52,10), ("cream",11,70), ("camera",32,30),
("tshirt",24,15), ("trousers",48,10), ("umbrella",73,40),
("trousers",42,70), ("overclothes",43,75), ("notecase",22,80),
("sunglasses",7,20), ("towel",18,12), ("socks",4,50), ("book",30,10)]
capacity = 200::Int
numKnapsacks = 3
stuff (name,w,v) left (v2,[]) = (v2,left)
stuff (name,w,v) left (v2,(cap, lst):xs) =
if w <= cap
then (v + v2, left ++ [(cap - w, (name,w,v):lst)] ++ xs)
else stuff (name,w,v) (left ++ [(cap,lst)]) (v2,xs)
knapsack = foldr addItem (repeat (0, replicate numKnapsacks (capacity,[])))
where addItem (name,w,v) list = left ++ zipWith max right newlist
where newlist = map (stuff (name,w,v) []) list
(left,right) = splitAt w list
main = print $ (knapsack inv) !! 600
ВЫХОД (общее значение, за которым следуют вес и вместимость каждого рюкзака):
*Main> main
(1062,[(1,[("map",9,150),("tshirt",24,15),("trousers",42,70),
("overclothes",43,75),("notecase",22,80),("sunglasses",7,20),
("towel",18,12),("socks",4,50),("book",30,10)]),
(0,[("compass",13,35),("cheese",23,30),("cream",11,70),
("camera",32,30),("trousers",48,10),("umbrella",73,40)]),
(1,[("sandwich",50,160),("glucose",15,60),("tin",68,45),("banana",27,60),
("apple",39,40)])])