我有以下问题:
你得到矩阵m * n,你必须找到从(1,1)到(x,y)的最大正数(子矩阵的所有元素应该> 0)子矩阵。< / p>
当你有以下矩阵时,maximal是什么意思:
[[1,2,3,4],[5,6,7,8],[9,10,-11,12],[13,14,15,16]]
然后最大正子矩阵是:
[[[1,2,3,4],[5,6,7,8]],[[1,2],[5,6],[9,10],[13,14]]]
即。前两行是一种解决方案,前两列是第二种解决方案。
另一个例子:矩阵
[[1,2,3,-4],[5,6,7,8],[-9,10,-11,12],[13,14,15,16]]
和解决方案是:
[[[1,2,3],[5,6,7]]]
这是我的Haskell程序解决了它:
import Data.List hiding (insert)
import qualified Data.Set as Set
unique :: Ord a => [a] -> [a]
unique = Set.toList . Set.fromList
subList::[[Int]] ->[[[Int]]]
subList matrix = filter (allPositiveMatrix) $ [ (submatrix matrix 1 1 x y) | x<-[1..width(matrix)], y<-[1..height(matrix)]]
maxWidthMat::[[[Int]]] -> Int
maxWidthMat subList =length ((foldl (\largestPreviousX nextMatrix -> if (length (nextMatrix!!0)) >(length (largestPreviousX !!0)) then nextMatrix else largestPreviousX ) [[]] subList)!!0)
maxWidthSubmatrices:: [[[Int]]] -> Int ->[[[Int]]]
maxWidthSubmatrices subList maxWidth = filter (\x -> (length $x!!0)==maxWidth) subList
height matrix = length matrix
width matrix = length (matrix!!0)
maximalPositiveSubmatrices matrix = maxWidthSubmatrices (subList matrix) (maxWidthMat (filter (\x -> (length $x!!0)==( maxWidthMat $ subList matrix )) (subList matrix)))
allPositiveList list = foldl (\x y -> if (y>0)&&(x==True) then True else False) True list
allPositiveMatrix:: [[Int]] -> Bool
allPositiveMatrix matrix = foldl (\ x y -> if (allPositiveList y)&&(x==True) then True else False ) True matrix
submatrix matrix x1 y1 x2 y2 = slice ( map (\x -> slice x x1 x2) matrix) y1 y2
slice list x y = drop (x-1) (take y list)
maximalWidthSubmatrix mm = maximum $ maximalPositiveSubmatrices mm
maximalHeigthSubmatrix mm = transpose $ maximum $ maximalPositiveSubmatrices $ transpose mm
-- solution
solution matrix =unique $ [maximalWidthSubmatrix matrix]++[maximalHeigthSubmatrix matrix]
你可以看到它非常冗长和丑陋。 它可能也不是最快。
您能否向我展示更多优雅,更快和更短解决方案(可能还有解释)?
答案 0 :(得分:0)
我认为,为了解决这个问题,我们首先要更好地进行降维:
reduce_dim :: (Num a,Ord a) => [[a]] -> [Int]
reduce_dim = map (length . takeWhile (>0)) -- O(m*n)
对于每一行,我们计算项目数 - 从左边开始 - 是正数。所以对于给定的矩阵:
1 2 3 4 | 4
5 6 7 8 | 4
9 10 -11 12 | 2
13 14 15 16 | 4
第二行因此映射到2,因为第三行是-11。
或者你的其他矩阵:
1 2 3 -4 | 3
5 6 7 8 | 4
-9 10 -11 12 | 0
13 14 15 16 | 4
由于第一行在第4列有-4,第三行在第1列有。
现在我们可以在这些行上获得scanl1 min
:
Prelude> scanl1 min [4,4,2,4] -- O(m)
[4,4,2,2]
Prelude> scanl1 min [3,4,0,4] -- O(m)
[3,3,0,0]
现在每次数字减少(并且结束时),我们知道我们在上面的行中找到了一个最大子矩阵。因为这意味着我们现在使用从哪里开始的行,列数更少。一旦我们达到零,我们就知道进一步的评估是没有意义的,因为我们正在处理一个有0列的矩阵。
因此,基于该列表,我们可以简单地生成最大子矩阵大小的元组列表:
max_sub_dim :: [Int] -> [(Int,Int)]
max_sub_dim = msd 1 -- O(m)
where msd r [] = []
msd r (0:_) = []
msd r [c] = [(r,c)]
msd r (c1:cs@(c2:_)) | c2 < c1 = (r,c1) : msd (r+1) cs
| otherwise = msd (r+1) cs
因此,对于您的两个矩阵,我们获得:
*Main> max_sub_dim $ scanl1 min $ reduce_dim [[1,2,3,4],[5,6,7,8],[9,10,-11,12],[13,14,15,16]]
[(2,4),(4,2)]
*Main> max_sub_dim $ scanl1 min $ reduce_dim [[1,2,3,-4],[5,6,7,8],[-9,10,-11,12],[13,14,15,16]]
[(2,3)]
现在我们只需要自己获取这些子矩阵。我们可以使用take
和map
而不是take
来执行此操作:
construct_sub :: [[a]] -> [(Int,Int)] -> [[[a]]]
construct_sub mat = map (\(r,c) -> take r (map (take c) mat)) -- O(m^2*n)
现在我们只需要在solve
:
-- complete program
reduce_dim :: (Num a,Ord a) => [[a]] -> [Int]
reduce_dim = map (length . takeWhile (>0))
max_sub_dim :: [Int] -> [(Int,Int)]
max_sub_dim = msd 1
where msd r [] = []
msd r (0:_) = []
msd r [c] = [(r,c)]
msd r (c1:cs@(c2:_)) | c2 < c1 = (r,c1) : msd (r+1) cs
| otherwise = msd (r+1) cs
construct_sub :: [[a]] -> [(Int,Int)] -> [[[a]]]
construct_sub mat = map (\(r,c) -> take r (map (take c) mat))
solve :: (Num a,Ord a) => [[a]] -> [[[a]]]
solve mat = construct_sub mat $ max_sub_dim $ scanl1 min $ reduce_dim mat
然后生成:
*Main> solve [[1,2,3,4],[5,6,7,8],[9,10,-11,12],[13,14,15,16]]
[[[1,2,3,4],[5,6,7,8]],[[1,2],[5,6],[9,10],[13,14]]]
*Main> solve [[1,2,3,-4],[5,6,7,8],[-9,10,-11,12],[13,14,15,16]]
[[[1,2,3],[5,6,7]]]
算法在 O(m×n)中运行 m 行数, n 列数,以构建矩阵的维度。对于每个定义的函数,我在评论中写下了时间复杂度。
将 O(m 2 ×n)带到构造所有子矩阵。所以算法运行在 O(m 2 ×n)。
我们可以转置方法并在列而不是行上运行。因此,如果我们使用矩阵与行数相差很大的矩阵,我们可以先计算最小值,可选择转置,从而使 m 成为两者中最小的一个。
我们可以通过构建子矩阵来加快算法的速度,同时构建max_sub_dim
来节省一些工作。