在MxN大小的棋盘上,有红色和绿色的块。
板上的每个正方形可以包含任意数量的任何颜色的块。 (我们可以在同一个正方形中有5个-3个绿色和2个红色,例如,绿色,红色或红色,或任意数字)
我正在寻找板上尽可能多的绿色碎片的轴对齐矩形。
但是,矩形所包含的红色块的数量不得超过给定数量。
矩形的一个角必须为(0,0)。
电路板尺寸为6x4,红色部分标记为“ x”,绿色部分标记为“ o”。
+-+-+-+-+-+-+ 3 | | | |o|x|o| +-+-+-+-+-+-+ 2 |o| |x| | |o| +-+-+-+-+-+-+ 1 |o| |o| |o|x| +-+-+-+-+-+-+ 0 | |o| |x| |x| +-+-+-+-+-+-+ 0 1 2 3 4 5
如果我们允许2个红色小块,那么(4,2)是一个最佳解决方案,因为(0,0)和(4,2)之间的区域包含5个绿色小块和2个红色小块。 最多有2个红色块的点最多包含5个绿色块。 (3,3)也是一个最佳解决方案。
如果我们允许3个红色小块,那么(4,3)是唯一的最佳解决方案,有6个绿色小块。
我得到了
:目标: 对于任何给定的“ maxRed”,该类应能够计算坐标(x,y),以使(0,0)和(x,y)之间的轴对齐矩形最多包含“ maxRed”个红色片断,并且绿色块的数量最大。
我的问题:
通过遍历所有具有最大绿色点和给定最大红色点的可能矩阵(以找到最大的三角形)来解决此问题显然效率低下,我正在尝试找到一种方法,无需使用蛮力
一些直觉:
我想看一下从原点(0,0)
起,允许在矩形内的壁橱最大红色点(如果maxRed = 2,则最接近两个红色点),然后检查所有可能的“扩展”从该点开始的矩形(仅宽度,高度,宽度和高度,等等。)的效率也不是我所相信的(在最坏的情况下它效率不高)。
然后我想也许maxRed等于2并找到最接近的两个红点,那么我们可以检查第三个maxRed在哪里(如果不存在,则将整个矩阵作为矩形返回),然后以某种方式搜索'较少的选项数量-仍然需要考虑所有情况(第三个点可以在当前矩形的顶部,或者从其左侧,或者对角线),以及是否来自例如在它的顶部,那么可能会有一个情况,我可以将其宽度扩展-也许不能(因为它的右边还有另一个红点)。但您明白了,以某种方式找到了一种并非蛮力且尽可能高效的算法。
问题2:另外一个我想知道如何解决的有趣问题: 如果坐标定义为“浮动”,这意味着该板没有网格,您将如何解决该问题。现在,您需要返回最佳浮点(x,y)坐标,以使(0,0)和(x,y)之间的区域最多包含“ maxRed”红色块,而绿色块的数目最大。您将如何找到它?复杂程度如何?
另一种直观的认识:
边缘情况:如果地图中的红点小于2,则返回所有矩阵的矩形。
然后,我们首先创建覆盖壁橱的两个红色点的矩形。 (例如,我们的矩形将扩展到y = 3,x = 2)覆盖了所有区域。
然后我们检查红色点的壁橱y轴大于当前矩形的y(y = 3),红色点的壁橱x轴又大于当前矩形的y轴矩形的x(x = 2),壁橱x和y也可以来自相同的第三个红色点,而不必来自两个不同的红色点。
这样,我们可以看到矩形可以延伸多远。
然后,在第3步中,如果可能,我们将尝试迭代上移(i + 1),并检查j的所有扩展,然后转到i + 2并检查所有j ..
4.1然后在可能的情况下右移(j + 1),并检查所有i,然后继续进行j + 2,依此类推。
并返回我们可以构建的最高矩形-并且在此过程中,我们不会检查太多的i和j。
但这还不够,
因为像“案例2”中那样存在边缘情况
在同一位置有2个红点,因此我们必须仔细检查第二远的红点(如果x或y或两者都比第一个壁橱的红点大得多)另一个红点,如果它在同一单元格中总共有两个红点,则我们将其扩展到x或y-并从那里继续向上/向下扩展。
(我们可以看到它是对角线,还是从右边还是从顶部),如果它是从第一个红点的右边(意味着x大于我们当前的x-仅在x轴上),那么我们可以检查我们可以延伸到顶部的距离-通过查看红色点列表(如果我们在顶部有红色点),如果没有,那么我们会一直延伸到终点,如果第二个红色点在我们顶部,则使用相同的方法,我们可以检查向右延伸多远。
,如果第二个红点在我们的对角线上(如用法示例中所示),那么我们将检查仅可以向左延伸多远,以及仅可以向顶部延伸多远,然后看看有什么对我们更好寻找更多的绿点。
我猜这种解决方案应该可以工作,并给出O(1)时间复杂度。
答案 0 :(得分:7)
如果您认为只有两个整数变量i
,j
和0 <= i <= M, 0 <= j <= N
,则可以使用动态编程来解决。在没有LaTeX引擎的情况下,我会尽量清楚地编写此代码,请耐心等待。
假设您创建四个M * N
整数G
,R
,V
和L
矩阵。对于每个点(i, j)
,g_ij
表示该正方形上的绿色块数,r_ij
表示红色块数。 v_ij
表示矩形(0, 0) - (i, j)
中的绿色块数,如果红色块的数量过多,则为0,而l_ij
表示矩形块中的红色块数(无穷大)如果原始值将超过限制。如果我说的是一个单元格的值,我的意思是v_ij
,则一个单元格的限制等于l_ij
。
从点(0, 0)
开始,编程方法如下:
(i, j)
(i + 1, j)
,向右为(i, j + 1)
。(i + i, j)
,矩形l_(i + 1)j
中红色部分的数量等于前一个单元格l_ij
的限制+上方单元格中红色部分的数量矩形,因此从(0, j)
到(i + 1, j)
的单元格。如果您使用限制l_ij
,则不必为一个步骤计算(i + 1) * j
个单元格的总和,而只需计算j + 1
个单元格中的j
个单元格的总和该行加上一个存储的值。计算v_(i + 1)j
的方法也一样,只需使用存储的值v_ij
加上上一行中绿色的数量即可。(i + 1, j)
和右上角(M, N)
之间创建一个矩形,并指定所有这些单元格的极限,因为所有可能的矩形可以形成的矩形必须包含矩形(0, 0) - (i + 1, j)
,因此它们必须包含太多红色块。正确的计算非常相似。对于第二个问题,可能的近似值是在0到1之间采用一个步长大小,然后将所有值除以该步长,然后向下舍入,因此(2/3, 7/5)
步长为1/10成为(6, 14)
。然后使用这些步骤应用算法。您可以多次运行,减小步长,在两次运行之间存储和转换矩阵V,因此可以避免进行大量计算。希望对您有所帮助!
更新:现在带有示例执行
例如,在每个单元格(x, y)
中分别表示绿色和红色的数量。矩阵G和R旁边是-空值表示0。
红件的数量限制为3。
G R
+-----+-----+-----+-----+ +---+---+---+---+ +---+---+---+---+
3 |(1,2)| | |(4,0)| 3 | 1 | | | 4 | 3 | 2 | | | |
+-----+-----+-----+-----+ +---+---+---+---+ +---+---+---+---+
2 | |(3,1)| |(1,2)| 2 | | 3 | | 1 | 2 | | 1 | | 2 |
+-----+-----+-----+-----+ +---+---+---+---+ +---+---+---+---+
1 |(1,1)|(1,1)| | | 1 | 1 | 1 | | | 1 | 1 | 1 | | |
+-----+-----+-----+-----+ +---+---+---+---+ +---+---+---+---+
0 | |(0,1)|(3,1)|(1,1)| 0 | | | 3 | 1 | 0 | | 1 | 1 | 1 |
+-----+-----+-----+-----+ +---+---+---+---+ +---+---+---+---+
0 1 2 3 0 1 2 3 0 1 2 3
从(0, 0)
开始,我们有0个红色块和0个绿色块,因此V
和L
如下所示:
V L
+---+---+---+---+ +---+---+---+---+
3 | | | | | 3 | | | | |
+---+---+---+---+ +---+---+---+---+
2 | | | | | 2 | | | | |
+---+---+---+---+ +---+---+---+---+
1 | | | | | 1 | | | | |
+---+---+---+---+ +---+---+---+---+
0 | 0 | | | | 0 | 0 | | | |
+---+---+---+---+ +---+---+---+---+
0 1 2 3 0 1 2 3
出于完整性考虑,我确实在V
和L
中填充了零值。
现在我们可以上到(1, 0)
,然后上到(0, 1)
。向上,我们得到l_10 = l_00 + r_10 = 0 + 1 = 1
和v_10 = v_00 + g_10 = 0 + 1 = 1
。在右侧,我们得到l_01 = l_00 + r_01 = 0 + 1 = 1
和v_01 = v_00 + g_01 = 0
。这给我们以下情况:
V L
+---+---+---+---+ +---+---+---+---+
3 | | | | | 3 | | | | |
+---+---+---+---+ +---+---+---+---+
2 | | | | | 2 | | | | |
+---+---+---+---+ +---+---+---+---+
1 | 1 | | | | 1 | 1 | | | |
+---+---+---+---+ +---+---+---+---+
0 | 0 | 0 | | | 0 | 0 | 1 | | |
+---+---+---+---+ +---+---+---+---+
0 1 2 3 0 1 2 3
我们现在可以从(1, 0)
开始,从(1, 0)
开始,这等效于从(0, 1)
开始,并且我们可以从(0, 1)
开始。
如果我们从(1, 0)
上升到(2, 0)
,则得到l_20 = l_10 + r_20 = 1 + 0 = 1
和v_20 = v_10 + g_20 = 1 + 0 = 1
。如果我们从(1, 0)
转到(1, 1)
,我们得到l_11 = l_10 + r_01 + r_11 = 1 + 1 + 1 = 3
。请注意,这与从(0, 1)
到l_11 = l_01 + r_10 + r_11 = 1 + 1 + 1 = 3
的上升完全相同。同样v_11 = v_10 + g_01 + g_11 = 1 + 1 = 2
。最后,如果我们从(0, 1)
转到(0, 2)
,我们将得到l_02 = l_01 + r_02 = 1 + 1 = 2
和v_02 = v_01 + g_02 = 0 + 3 = 3
。这将导致以下模式:
V L
+---+---+---+---+ +---+---+---+---+
3 | | | | | 3 | | | | |
+---+---+---+---+ +---+---+---+---+
2 | 1 | | | | 2 | 1 | | | |
+---+---+---+---+ +---+---+---+---+
1 | 1 | 2 | | | 1 | 1 | 3 | | |
+---+---+---+---+ +---+---+---+---+
0 | 0 | 0 | 3 | | 0 | 0 | 1 | 2 | |
+---+---+---+---+ +---+---+---+---+
0 1 2 3 0 1 2 3
我们现在可以从(2, 0)
开始,从(2, 0)
开始,这等效于从(1, 1)
开始,从(1, 1)
开始,等同于上升来自(0, 2)
,或者直接来自(0, 2)
。如果我们从(2, 0)
上升到(3, 0)
,则得到l_30 = l_20 + r_30 = 1 + 2 = 3
和v_30 = v_20 + g_30 = 1 + 1 = 2
。
我们可以通过从(2, 1)
向上计算(2, 0)
,但是从(1, 1)
向上可以允许我们对预先计算的矩形的较大部分(4个单元格而不是3个单元格)进行相同的计算)。我们得到l_21 = l_11 + r_20 + r_21 = 3 + 0 + 1 = 4
。由于超出限制,v_21 = 0
。现在,任何包含(2, 1)
的矩形都将具有与(2, 1)
完全相同的单元格,包括那些加起来最多为4个红色部分的单元格。因此,所有包含(2, 1)
的矩形都必须无效。这些都是带有x >= 2
和y >=1
的单元格。为了明确起见,我们给它们l_xy = Inf
。
单元格(1, 2)
包含(0, 0)
,但是由于l_12 = l_11 + r_02 + r_12 = 3 + 1 + 0 = 4
,因此该单元格无效,(1, 3)
遵循与上述相同的逻辑。
最后,如果我们从(0, 2)
转到(0, 3)
,我们将得到l_03 = l_02 + r_03 = 2 + 1 = 3
和v_03 = v_02 + g_03 = 3 + 1 = 4
。这将导致以下模式:
V L
+---+---+---+---+ +---+---+---+---+
3 | 2 | 0 | 0 | 0 | 3 | 3 |Inf|Inf|Inf|
+---+---+---+---+ +---+---+---+---+
2 | 1 | 0 | 0 | 0 | 2 | 1 |Inf|Inf|Inf|
+---+---+---+---+ +---+---+---+---+
1 | 1 | 2 | 0 | 0 | 1 | 1 | 3 |Inf|Inf|
+---+---+---+---+ +---+---+---+---+
0 | 0 | 0 | 3 | 4 | 0 | 0 | 1 | 2 | 3 |
+---+---+---+---+ +---+---+---+---+
0 1 2 3 0 1 2 3
因此,如您所见,具有最高值的矩形是由点(0, 3)
组成的,值为4的矩形,我们在计算16个像元中只有10个时发现了这一点。当然,该算法的上限是O(MN)
,但实际上这是不可避免的,因为考虑没有红色碎片或限制很高的情况。然后,您仍然必须计算所有M * N
个单元格的总和。
答案 1 :(得分:3)
由于角(0,0)是矩形的必需部分,因此整个任务非常简单。算法如下:
假设X,Y是木板的尺寸:
green_counts, red_counts = count_pieces()
found_pos = None
found_count = 0
for y in range(0, Y):
x = find_x_for_y_with_max_red_pieces()
g = green_counts(x, y)
if g > found_count:
found_count = g
found_pos = x, y
print(found_pos)
首先,我们为矩形(0,0)->(x,y)创建带有红色和绿色计数的二维数组。然后我们迭代y。对于每个y,我们找到最大的x,满足红色的limis。我们计算绿色件数,并检查是否比以前更好。整个过程将在O(n^2)
中运行,因为您需要计算件数。
请注意,您可以进一步提高内存需求(不需要完整的二维数组,只需要“上一个”行)。
问题2:如何处理浮动仓位?相同。对x个位置进行排序,然后将其替换为index。因此,例如对于(0,0),(0.2,1),(0.2,2.5),(0.4,1)点,您将获得(0,0),(1、1),(1、2),( 2,1)。然后使用上面的算法。
答案 2 :(得分:3)
这是一种算法,它通过扫描片段而不是网格来工作。我认为它在O(p log p)中运行,其中p是件数,而不是O(grid_size ^ 2)。
这是一种双扫线算法。想象两条线,一条水平线定义矩形的顶部(代码中的top
)和一条垂直线(x
)定义右侧。顶线从y轴的网格顶部开始,而垂直线从y轴开始。垂直线向右掠过,当到达一块时采取动作。如果该部分位于顶行下方,则该部分将添加到当前矩形中。如果会有过多的红色碎片,则将水平线向下扫动,直到红色碎片的数量在限制范围内。水平线处或上方的所有绿色部分都将被删除,因为它们不在新的矩形中。向下移动矩形的顶部之前,请先检查绿色部分的数量,看看是否达到新的最大值。
类似于python的range()
排除上限的方式,矩形包括(0,0)但不包括函数返回的边界。例如,测试用例返回((4,4),5),这意味着由0 <= x <4和0 <= y <4定义的矩形有5个绿色小块(注意上限为“ <”)。对于整数坐标,矩形为(0,0)到(3,3)(含)。对于浮点坐标,上限不包括给定点。
import sortedcontainers as sc # http://www.grantjenks.com/docs/sortedcontainers/
X,Y,Color = 0,1,2
def solve(pieces, size, max_reds):
# Sort pieces by x, then red before green, then bottom-to-top
pieces.sort(key=lambda t:(t[X],t[Color]=='g',t[Y]))
# These keep track of the pieces that are in the rectangle. They
# are sorted by 'y' value, so it is easy to remove pieces when
# 'top' is lowered due to too many reds in the rectangle.
reds_in_rect = sc.SortedKeyList(key=lambda t:t[Y])
greens_in_rect = sc.SortedKeyList(key=lambda t:t[Y])
# For keeping track of the maximum number of green
# pieces and the corresponding coordinates.
max_greens = 0
max_x = 0
max_y = 0
# 'top' scans from top to bottom and represents the top of
# the current rectangle. It gets lowered to remove red pieces
# from the rectangle when there are too many of them.
top = size[Y]
# The pieces are sorted so this loop scans the pieces left-to-right.
# If multiple pieces have the same x-coordinate, red ones come before
# green ones, then lower ones before higher ones.
for x,y,p in pieces:
# Only pieces below the top of the rectangle are considered.
# And they are added to the rectangle
if y < top:
if p == 'g':
greens_in_rect.add((x,y,p))
elif p == 'r':
reds_in_rect.add((x,y,p))
# If there are too many red pieces in the rectangle, 'top' gets
# lowered to the 'y' value of the top-most red piece. Then any
# red or green pieces at or above the new 'top' get removed from
# the rectangle.
if len(reds_in_rect) > max_reds:
# before lowering the top of the rectangle check if current
# rectangle has a maximum number of green pieces
if len(greens_in_rect) > max_greens:
max_greens = len(greens_in_rect)
max_x = x
max_y = top
# lower to top to the 'y' value of the highest
# red piece seen so far
top = reds_in_rect[-1][Y]
# remove any red pieces at or above the top
# of the new rectangle
while reds_in_rect and reds_in_rect[-1][Y] >= top:
reds_in_rect.pop()
# remove any green pieces at or above the top
# of the new rectangle
while greens_in_rect and greens_in_rect[-1][Y] >= top:
greens_in_rect.pop()
# last check of the number of green pieces
if len(greens_in_rect) > max_greens:
max_greens = len(greens_in_rect)
max_x = size[X]
max_y = top
return (max_x, max_y), max_greens
测试案例:
# +-+-+-+-+-+-+
# 3 | | | |o|x|o|
# +-+-+-+-+-+-+
# 2 |o| |x| | |o|
# +-+-+-+-+-+-+
# 1 |o| |o| |o|x|
# +-+-+-+-+-+-+
# 0 | |o| |x| |x|
# +-+-+-+-+-+-+
# 0 1 2 3 4 5
size = 6,4
max_reds = 2
red = [(3,0), (5,0), (5,1), (2,2), (4,3)]
green = [(1,0), (0,1), (2,1), (4,1), (0,2), (5,2), (3,3), (5,3)]
pieces = [(x,y,'r') for x,y in red] + [(x,y,'g') for x,y in green]
solve(pieces, size, max_reds) # --> returns ((4,5),5)