评估NxN矩阵中最多包含K个(随机放置)的最大正方形的面积

时间:2019-07-09 16:49:01

标签: c++ search matrix binary square

我有一个解决方案,围绕找到在NxN矩阵中最多包含K个(其余为零,N <= 2000)的最大正方形的面积。 W的个数W随机分布在矩阵上(因此,K <= W表示)。我已经使用二进制搜索方法解决了问题-关键是赋值还告诉我要在不到2秒的计算时间内解决问题,而我的速度还不够快。

我尝试过的方法: 从下限1和上限N-1(最大面积为N * N,因为它仅在K> = W时才发生)开始,在正方形大小上执行二进制搜索算法。当找到一个正方形,正方形大小(上+下)最多包含K个正方形时,它会将边界向上移动,否则向下移动-此测试功能可能是导致程序运行时间过长的原因,例如在最坏的情况下,仍然需要O(N²)时间检查一个平方大小。可悲的是,我对二进制/ n元搜索还很陌生,还没有真正的方法来使它更快。我想知道三元/ n元搜索是否会有所帮助。我也读过有关并行二进制搜索的信息,但是我不确定如何实现它。任何帮助将不胜感激。

可悲的是,我现在无法像在办公室计算机上那样提供代码,但是我正在寻找关于如何解决问题的更一般的想法,而不是任何具体的实现。

2 个答案:

答案 0 :(得分:0)

是的,它来自在线评审系统(domjudge)。我可能应该提到它应该在2秒内计算20个测试用例,因此为什么2秒可能不够用。

至于代码,我很遗憾目前无法提供。谢谢,不管答案如何!

答案 1 :(得分:0)

正如评论中指出的那样,除非我们知道在线法官使用哪种硬件,否则我们不知道O(n ^ 2 log n)解决方案是否可以通过。 O(n ^ 2 log n)解决方案可能应该可以通过,但是您以使常数因子过大的方式编写了该解决方案,例如,以错误的方式遍历矩阵(结果是持续的高速缓存未命中)。因此,在这种情况下,有必要查看您的代码以诊断任何性能错误。但是,也许法官是在某些不应该通过O(n ^ 2 log n)的旧机器上。在这种情况下,您可以尝试O(n ^ 2)解决方案。我将在下面描述。

对于N x N网格中的每个正方形(i,j),我们将计算最大的M[i][j],使得边长为M[i][j]的正方形的右下角为(i, j)最多有K个。另外,我们将在该正方形中存储一个数-称为O[i][j]。这样,整个问题实例的答案就是所有(i,j)的最大值M[i][j]

要计算这些M[i][j]O[i][j]的值,我们使用以下算法:

// precompute row and column partial sums
int bestM = 0;
for (int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
        if (i == 0 || j == 0) {
            // this case is trivial 
        } else {
            // use the values of M[i-1][j-1] and O[i-1][j-1] to compute M[i][j] and O[i][j]
        }
        bestM = std::max(bestM, M[i][j]);
    }
}
return bestM;

要从M[i][j]O[i][j]计算M[i-1][j-1]O[i-1][j-1],请按以下步骤进行:我们知道M[i][j]至多M[i-1][j-1] + 1 (因为边长M[i-1][j-1] + 2的正方形在(i,j)的右下角将严格包含边长M[i-1][j-1] + 1的正方形在(i-1,j-1)的右下角,但该正方形太大了。因此,我们有一个内部循环:

int o = /* number of ones in square of side length M[i-1][j-1] + 1 with lower right corner at (i, j) */;
for (int k = M[i-1][j-1] + 1; k >= 0; k--) {
    if (o <= K) {
        O[i][j] = o;
        M[i][j] = k;
        break;
    } else {
        // reduce o to be the number of ones in square of side length k-1 with lower right corner at (i, j)
    }
}

要有效地计算o,我们观察到边长M[i-1][j-1] + 1的右下角位于(i,j)的平方只是以下项的不相交并集:

  1. 边长M[i-1][j-1]的右下角位于(i-1,j-1)的平方,和
  2. 由正方形的下边缘和右边缘组成的L形,顶点位于(i,j)

因此,o的初始值只是区域1中的个数O[i-1][j-1]与区域2中的个数之和。我们可以在恒定时间内获得后者如果我们已经按照第一行伪代码块中的指示,沿每一行和每一列预先计算了部分和。同样,每次o都需要在内部循环中减少时,我们只需要减去在L形(边长为{{1}的正方形的上边缘和左边缘)中找到的个数},我们同样可以使用部分和。

尽管有3个嵌套循环,总运行时间仍为O(N ^ 2),因为当前正在评估的正方形的左上角从不向后移动,因此内部循环所花费的总时间正方形的每个对角线在该对角线的大小上都是线性的,这意味着总的运行时间在正方形的总数上是线性的。