【问题标题】:The greatest sum in part of matrix array矩阵数组部分的最大和
【发布时间】:2017-01-28 23:20:12
【问题描述】:

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

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

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

  1. 制作附加矩阵数组 (sum[N][M]) 和整数 solution = infinity
  2. 对于每个 1 i N + 1 和 1 j M + 1

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

    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) >= k 那么 solution = current_solution 和 (a - x) * (b - y) 的最小值

但是这个解决方案很慢(四次时间),有没有可能让它更快?我正在寻找迭代的对数时间(或更差/更好)。我设法减少了我的时间,但没有大幅减少。

【问题讨论】:

  • 矩阵可以包含负值吗? (我知道你说的是整数但只是检查)
  • 哦...对了,我忘了说,只有 intigers >= 0

标签: arrays algorithm matrix time-complexity


【解决方案1】:

如果矩阵只包含 >= 0 的值,则一维情况下存在线性时间解,可以扩展到二维情况下的三次时间解。

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

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

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

如果允许负值,这将不起作用,因为在第二步中,您需要能够假设缩小间隔总是会导致总和更小,因此当总和低于 k 时,您知道这是可能的最小对于给定的区间端点。

对于 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
        }
    }
}

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

    • 谢谢你的回答,真的很有帮助:)
    猜你喜欢
    • 2013-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-24
    • 2020-05-27
    • 1970-01-01
    相关资源
    最近更新 更多