【发布时间】:2021-01-14 14:32:44
【问题描述】:
我正在尝试解决竞赛中的一个动态编程问题,这自然会限制执行时间和内存。粗略地说,该问题的剥离版本是首先按行生成一个表,然后处理每一列,这涉及将元素与其垂直相对的对应元素“配对”。这使我认为在内存中维护整个表是必要的。使用数组后,按照here的建议,速度还可以,但程序仍然占用内存。
问题
给定两个整数k 和n,定义
dp[j][0] = dp[j][n+1] = 0 for j=0,...k,
dp[0][m] = 1 for m=1,...,n,
dp[j][m] = dp[j-1][m-1] + dp[j-1][m+1] for j=0,...,k and m=1,...,n.
让vs[m] = sum [dp[j][m] * dp[k-j][m] | j<-[0..k]] 为所有m=1,...,n。
我需要计算vs[1],...,vs[n],下面是我的代码。 (即solve 函数。由于结果可能是大数,我们以10^9+7 为模计算它们。)
{-# LANGUAGE Safe #-}
{-# OPTIONS_GHC -O2 #-}
import safe Control.Arrow ((>>>))
import qualified Data.Text as T
import qualified Data.Text.IO as TI
import qualified Data.Map as M
import qualified Data.Array as A
import Data.List
import Data.Function
main :: IO ()
main = TI.getContents >>= (T.lines >>> drop 0 >>> tcio >>> (mapM_ putStrLn)) where
tcio :: [T.Text] -> [String]
tcio [] = []
tcio (nkq : rest) = ((:[]). istoline . solve . linetois) nkq ++ tcio rest;
linetoi t = f 0 t where f n t = if (T.null) t then n else f (10*n + (on (-) fromEnum (T.head t) '0') ) (T.tail t) ;
linetois = (map linetoi).(T.words); linestoiss = map linetois;
itoline = show; istoline = unwords . (map itoline); isstolines = map istoline
solve :: [Int] -> [Int]
solve [n,k,_] = vs where
dpf 0 m = if m==0 || m==n+1 then 0 else 1
dpf j m = if m==0 || m==n+1 then 0 else (dp A.! (j-1) A.! (m-1)) `madd` (dp A.! (j-1) A.! (m+1))
dp = A.listArray (0,k) [(A.listArray (0,n+1) [dpf j m | m <- [0..(n+1)]]) | j<-[0..k]]
vs = [foldl1' madd ([(dp A.! j A.! m) `mmult` (dp A.! (k-j) A.! m) | j<-[0..k]]) | m<-[0..(n+1)]]
madd = modp (+)
mmult a b = fromInteger $ modp (*) (toInteger a) (toInteger b)
modp f a b = (f a b)`mod` (10^9+7)
问题
对于k=5000 和n=1000,它消耗超过2GB 的内存!考虑到比赛中设置的限制为 1GB 的实际问题,这远高于 1 GB。
分析结果是here。我想知道我是否有效地使用了数组结构。提供给A.listArray 的列表理解是否暗示在内部创建一个二维列表,有点违背目的?如果有的话,我们还能如何优化内存?
【问题讨论】:
-
顺便说一句,转换为
Integer并返回是不必要的。Int在现代机器上是64位,存储(10^9+7)^2绰绰有余;如果您是偏执狂,请导入Data.Int并使用Int64甚至可以在旧机器上工作。 -
我没有发现
Int64在算术和 IO/解析方面优于Integer。
标签: haskell memory multidimensional-array dynamic-programming