我已经在 Haskell 中编写了0-1 背包问题。我对迄今为止所取得的懒惰和普遍性水平感到相当自豪。
我首先提供用于创建和处理惰性二维矩阵的函数。
mkList f = map f [0..]
mkTable f = mkList (\i -> mkList (\j -> f i j))
tableIndex table i j = table !! i !! j
然后我为给定的背包问题制作一个特定的表
knapsackTable = mkTable f
where f 0 _ = 0
f _ 0 = 0
f i j | ws!!i > j = leaveI
| otherwise = max takeI leaveI
where takeI = tableIndex knapsackTable (i-1) (j-(ws!!i)) + vs!!i
leaveI = tableIndex knapsackTable (i-1) j
-- weight value pairs; item i has weight ws!!i and value vs!!i
ws = [0,1,2, 5, 6, 7] -- weights
vs = [0,1,7,11,21,31] -- values
最后用几个辅助函数来查看表格
viewTable table maxI maxJ = take (maxI+1) . map (take (maxJ+1)) $ table
printTable table maxI maxJ = mapM_ print $ viewTable table maxI maxJ
这很容易。但我想更进一步。
我想要一个更好的表数据结构。理想情况下,它应该是
未装箱(不可变)[编辑] 没关系- 懒惰的
- 无界
O(1)
建设时间O(1)
查找给定条目的时间复杂度,
(更实际地,在最坏的情况O(log n)
下,其中 ni*j
用于查找第 i 行第 j 列的条目)
如果您能解释您的解决方案为什么/如何满足这些理想,则可以加分。
如果您可以进一步概括knapsackTable
,也可以加分,并证明它是有效的。
在改进数据结构时,您应该尝试满足以下目标:
- 如果我要求最大权重为 10 的解决方案(在我当前的代码中,即
indexTable knapsackTable 5 10
5 意味着包括项目 1-5),则只应执行最少的工作量。理想情况下,这意味着无需O(i*j)
将表格的每一行的脊椎强制为必要的列长度。如果您认为 DP 意味着评估整个表格,您可以说这不是“真正的”DP。 - 如果我要求打印整个表格(类似于
printTable knapsackTable 5 10
),则每个条目的值应计算一次且仅计算一次。给定单元格的值应取决于其他单元格的值(DP 风格:想法是,永远不要重新计算相同的子问题两次)
想法:
- Data.Array有界:(
- UArray严格:(
- 记忆技术(关于Haskell中DP的问题)这可能有效
对我陈述的理想做出一些妥协的答案将被赞成(无论如何,由我),只要它们提供信息。妥协最少的答案可能是“接受”的答案。