SPOILERS:我正在研究http://www.spoj.pl/problems/KNAPSACK/所以如果你不想为你破坏可能的解决方案,不要偷看。
样板:
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,我只是将必要的部分复制并粘贴到提交的源中)时,它超出了时间限制。 = /
我不明白为什么;我asked earlier关于执行0-1背包的有效方法,我相信它的速度和它一样快:一个memoized函数,只能递归地计算它绝对需要的子条目为了产生正确的结果。我是否以某种方式弄乱了备忘录?我错过了这段代码的慢点吗? SPOJ是否只是偏向于Haskell?
我甚至将{-# OPTIONS_GHC -O2 #-}
放在提交的顶部,但唉,它没有帮助。我尝试过使用Sequence
s的2D数组的类似解决方案,但它也被拒绝为太慢了。
答案 0 :(得分:4)
有一个主要问题确实减慢了这个问题。它太多态了。类型专用版本的函数可以比多态变种快得多,并且无论出于何种原因,GHC都没有将此代码内联到可以确定使用的确切类型的程度。当我将integral
的定义更改为:
integral :: Memo Int
integral = wrap id id bits
我获得了大约5倍的加速;我认为它足够快,可以在SPOJ上被接受。
然而,这仍然比gorlum0的解决方案慢得多。我怀疑原因是因为他正在使用数组而你使用自定义trie类型。使用trie将占用更多内存,并且由于额外的间接,缓存未命中等等,也会使查找速度变慢。如果你在IntMap中对字段进行严格修改和取消设置,你可能能够弥补很多差异,但我不确定这是可能的。试图在BitTrie
中对字段进行严格修改会为我创建运行时崩溃。
答案 1 :(得分:0)
减缓Haskell的一件事是IO,Haskell中的String类型提供了我们对SPOJ不需要的UTF8支持。 ByteStrings非常快,所以你可能想考虑使用它们。