SPOILERS:我正在研究http://www.spoj.pl/problems/KNAPSACK/,所以如果你不想为你宠坏一个可能的解决方案,请不要偷看。对于SPOJ,这个备忘录DP表太慢了怎么办?
的样板:
import Data.Sequence (index, fromList)
import Data.MemoCombinators (memo2, integral)
main = interact knapsackStr
knapsackStr :: String -> String
knapsackStr str = show $ knapsack items capacity numItems
where [capacity, numItems] = map read . words $ head ls
ls = lines str
items = map (makeItem . words) $ take numItems $ tail ls
某些类型和助手设置阶段:
type Item = (Weight, Value)
type Weight = Int
type Value = Int
weight :: Item -> Weight
weight = fst
value :: Item -> Value
value = snd
makeItem :: [String] -> Item
makeItem [w, v] = (read w, read v)
而且主要功能:
knapsack :: [Item] -> Weight -> Int -> Value
knapsack itemsList = go
where go = memo2 integral integral knapsack'
items = fromList $ (0,0):itemsList
knapsack' 0 _ = 0
knapsack' _ 0 = 0
knapsack' w i | wi > w = exclude
| otherwise = max exclude include
where wi = weight item
vi = value item
item = items `index` i
exclude = go w (i-1)
include = go (w-wi) (i-1) + vi
而且此代码的工作;我试过插入SPOJ示例测试用例,它会产生正确的结果。但是,当我将这个解决方案提交给SPOJ(而不是导入Luke Palmer的MemoCombinators时,我只需将所需的部分复制并粘贴到提交的源代码中),它就超出了时间限制。 =/
我不明白为什么; I asked earlier关于执行0-1背包的有效方法,并且我相当确信它的速度和它获得的一样快:一个memoized函数,它只会递归地计算它绝对需要的子条目,以便生成正确的结果。莫名其妙地搞乱了记忆吗?这段代码中有一个缓慢的地方,我错过了? SPOJ是否对Haskell有偏见?
我甚至把{-# OPTIONS_GHC -O2 #-}
放在提交的顶部,但唉,它没有帮助。我试过了一个类似的解决方案,它使用了一个二维数组Sequence
s,但它也被拒绝太慢。
您是否尝试过分析?总的来说,我还没有发现memoisation对于大多数任务来说是一个很大的帮助。另外,使用序列在这里似乎没有多大的帮助,也许是一个'Map'呢? – ivanm
你能告诉我们所提供的memotries的必要位的完整代码吗?这将使诊断问题更容易。 –
'索引'操作是'O(log(min(i,n-i)))'(根据haddock)。也许你应该使用一个数组或向量。 –