我在描述用于查找二进制数据的最大矩形区域的算法时遇到问题,其中1比k更频繁地出现k次。数据总是n ^ 2位,如下所示:
例如,n = 4的数据如下所示:
1 0 1 0
0 0 1 1
0 1 1 1
1 1 0 1
k的值可以是1 .. j(k = 1表示0和1的数量相等)。
对于上述数据示例,对于k = 1,解决方案是:
1 0 1 0 < - 4 x'0'和4 x'1'
0 0 1 1
0 1 1 1
1 1 0 1
但在这个例子中:
1 1 1 0
0 1 0 0
0 0 0 0
0 1 1 1
解决方案是:
1 1 1 0
0 1 0 0
0 0 0 0
0 1 1 1
我试过几个暴力算法,但是对于n> 20它变得太慢了。你能告诉我如何解决这个问题吗?
正如RBerteig所提出的那样 - 问题也可以这样描述:“在一个给定的方形位图中,通过某个任意过程将单元格设置为1或0,找到最大的矩形区域,其中1和0以指定的比例出现, K“。
答案 0 :(得分:3)
Bruteforce在这里应该做得很好n< 100,如果正确实现:下面的解决方案具有O(n ^ 4)时间和O(n ^ 2)存储器复杂度。在现代PC上,10 ^ 8的操作应该远低于1秒(特别是考虑到每个操作都很便宜:很少添加和减少)。
一些观察
因此,问题简化为:创建数据结构,允许在恒定时间内找到每个子矩形中的1个数。
现在,假设我们有子矩形[i0..i1]x[j0..j1]
。即,它占据i0和i1之间的行以及j0和j1之间的列。让count_ones
成为计算子矩形中1的数量的函数。
这是主要观察结果:
count_ones([i0..i1]x[j0..j1]) = count_ones([0..i1]x[0..j1]) - count_ones([0..i0 - 1]x[0..j1]) - count_ones([0..i1]x[0..j0 - 1]) + count_ones([0..i0 - 1]x[0..j0 - 1])
与实际例子相同:
AAAABBB
AAAABBB
CCCCDDD
CCCCDDD
CCCCDDD
CCCCDDD
如果我们需要在D子矩形(3x4)中找到1的数量,我们可以通过在整个矩形(A + B + C + D)中取1的数量,减去1的数量(A) + B)矩形,在(A + C)矩形中减去1的数字,在(A)矩形中添加1的数字。 (A + B + C + D) - (A + B) - (A + C) + (A) = D
因此,对于每个sums
和i
,在子矩形j
中包含1的1,我们需要表[0..i][0..j]
。
您可以在O(n ^ 2)中创建此表,但即使是填充它的直接方式(对于每个i
和j
迭代[0..i][0..j]
区域的所有元素)也将是O( N R个4)。
有了这张表,
count_ones([i0..i1]x[j0..j1]) = sums[i1][j1] - sums[i0 - 1][j1] - sums[i1][j0 - 1] + sums[i0 - 1][j0 - 1]
因此,达到了时间复杂度O(n ^ 4)。
答案 1 :(得分:2)
这仍然是蛮力,但您应该注意的是,您不必从头开始重新计算新的i*j
矩形。相反,对于每个可能的矩形大小,您可以一次一步地在n*n
网格上移动矩形,减少矩形内不再位的计数,并递增新输入矩形的位数。您可以将其与变化的矩形大小结合起来,并尝试找到移动和调整矩形大小的最佳模式。
答案 2 :(得分:0)
只是一些提示..
您可以对值施加更好的限制。要求导致条件
N1*(k+1) == S*k
,其中N1
是区域中的数量,S=dx*dy
是其表面。
它可以以更好的形式重写:
N1/k == S/(k+1)
。
由于数字n
和n+1
的最大公约数始终为1,因此N1
必须是k
和dx*dy
的倍数才是多个k+1
。它大大减少了解决方案的可能空间,k
越大越好(对于dx*dy
,您需要使用k+1
的素数除数。
现在,因为你只需要具有这种属性的最大区域的表面,从最大的区域开始并移动到较小的区域是明智的。通过尝试满足除数和边界条件的dx*dy
downto n^2
k+1
,你会发现解决方案非常快,比O(n ^ 4)快,因为一个特殊的原因:除了特殊构造数组的情况外,如果我们假设一个随机输入,N1
区域中有S
个(n-dx+1)*(n-dy+1)
个值的概率是表面S
将随着S
的减少而不断增长。 (k
的较大值会使概率变小,但同时它们会使dx
和dy
对的过滤器变得更强。
此外,这个问题:http://ioinformatics.org/locations/ioi99/contest/land/land.shtml,看起来有点类似,也许你会在他们的解决方案中找到一些想法。