矩阵数组中最大的一部分

时间:2017-01-28 23:20:12

标签: arrays algorithm matrix time-complexity

我遇到了一个简单的问题,我正在寻找一个比我更好的解决方案。

我有一个整数矩阵数组( tab [N] [M] )和整数( k ),我必须找到最小的矩形(子矩阵数组)它的元素总和大于 k

所以,我目前的解决方案是:

  1. 制作额外的矩阵数组( sum [N] [M] )和整数 =无穷大
  2. 对于每个1&lt; i &lt; = N + 1并且<&lt; j &lt; = M + 1

    sum[ i ][ j ] = sum[ i - 1 ][ j ] + sum [ i ][ j - 1] + tab[ i  ] [ j ] - sum[ i - 1] [ j - 1]  
    
  3. 然后查看每个矩形f.e矩形,从(x,y)开始,结束(a,b)

    Rectangle_(x,y)_(a,b) = sum[ a ][ b ] - sum[ a - x ] [ b ] - sum[ a ][ b - y ] + sum[ a - x ][ b - y ]
    

    如果Rectangle_(x,y)_(a,b)&gt; = k则解决方案= current_solution的最小值和(a - x)*(b - y)

  4. 但是这个解决方案很慢(四分之一时间),有没有可能让它更快?我正在寻找迭代对数时间(或更糟/更好)。我设法减少了我的时间,但并没有实质性的。

2 个答案:

答案 0 :(得分:0)

我已经看到了矩阵矩形问题的一些答案,这些问题通过求解类似的一维问题然后将其应用于矩阵的每一行来实现,每行由两个相邻行的总和形成,每个总和来自三个相邻的行,依此类推。因此,尝试找到至少具有给定总和的线中的最小间隔。 (显然,如果您的矩阵又高又瘦而不是短而胖,那么您可以使用列而不是行)

从左到右工作,保持到目前为止看到的值的所有前缀的总和,直到当前位置。以位置结束的间隔的值是直到并包括该位置的总和减去在间隔开始之前结束的前缀的总和。因此,如果您保留一个前缀列表,并且在当前位置之前,您可以在每个点找到在该点超过阈值的最短间隔。我将在下一段中解释如何有效地搜索它。

事实上,您可能不需要所有前缀总和的列表。较小的前缀和更有价值,并且更进一步结束的前缀和更有价值。因此任何前缀和在另一个前缀sum之前结束并且也大于其他前缀和的前缀总和是没有意义的。因此,您想要的前缀总和可以排列成一个列表,该列表保留计算它们的顺序,但也具有每个前缀总和小于其右侧前缀总和的属性。这意味着当您想要找到最接近的前缀总和时,您最多可以通过二进制搜索来执行此操作。这也意味着当你计算一个新的前缀和时,你可以把它放到列表中的位置,只需丢弃列表右边的所有前缀和,大于或等于它。

答案 1 :(得分:0)

如果矩阵仅包含值> = 0,则在1D情况下存在线性时间解决方案,可以在2D情况下扩展到立方时间解决方案。

对于1D情况,你从左到右进行一次传递,在数组中滑动一个窗口,随着时间的推移拉伸或收缩,以便间隔中包含的数字总和至少为k(或突破如果这是不可能的循环)。

最初,将间隔的左索引边界设置为第一个元素,右边索引绑定到-1,然后循环:

  • 将右边界增加1,然后继续增加,直到间隔内的值总和为&gt; k,或到达阵列的末尾。
  • 增加左边界以尽可能小地缩小间隔,而不会使值总和小于或等于k。
  • 如果结果是有效间隔(意味着第一步没有找到有效间隔而未到达数组的末尾),则将其与最小值进行比较,并在必要时进行更新。

如果允许负值,这不起作用,因为在第二步中你需要能够假设收缩间隔总是导致较小的总和,所以当总和低于k时你知道&# 39; s对于给定的间隔终点是最小的。

对于2D情况,您可以迭代所有可能的子矩阵高度,并遍历给定高度的每个可能的起始行,并为每一行执行此水平扫描。

在伪代码中:

假设您有一个函数rectangle_sum(x, y, a, b),它返回从(x,y)到(a,b)的值之和,并在O(1)时间内运行,使用求和区域表。

for(height = 1; height <= M; height++)  // iterate over submatrix heights
{
    for(row = 0; row <= (M-h); row++)   // iterate over all rows
    {
        start = 0; end = -1; // initialize interval
        while(end < N)  // iterate across the row
        {
            valid_interval = false;
            // increment end until the interval sums to > k:
            while(end < (N-1))
            {
                end = end + 1;
                if(rectangle_sum(start, row, end, row + height) > k)
                {
                    valid_interval = true;
                    break;
                }
            }
            if(!valid_interval)
                break;
            // shrink interval by incrementing start:
            while((start < end) && 
                rectangle_sum(start+1, row, end, row + height) > k))
                start = start + 1;

            compare (start, row), (end, row + height) with current smallest
            submatrix and make it the new current if it is smaller
        }
    }
}