算法编程题解答
朋友的题目
今天朋友遇到了一个算法题目,感觉很有意思,在这里稍微分享解答一下。但是我的答案效率并不很高,就当是抛砖引玉,希望有助于大家!也希望有兴趣的大神指点一下~
题目
如下图,有一个长为L,宽为W的矩形,L为矩形两个短边中点的连线。在L上依次分布n个点a1, a2, …, an(其中 0<= ai <= l)。
现在以这n个点为圆心画圆,要求所有的圆与圆、圆与矩形框之间不能相交,请给出每个点对应的圆的半径,是的所有圆的面积最大。
思路
个人认为,最大圆的面积的关键在于,依次填充半径最大的圆。如下图所示,假设矩形框长160,宽100,那么下图一中圆的面积为
area1 = pi * (50^2 + 30^2) = 3400pi
.
图二中的圆的面积为area1 = pi * (40^2 + 40^2) = 3200pi
答案
import cv2 as cv
import numpy as np
class MaxCircleAreaInRect:
def __init__(self):
np.random.seed(0)
self.h = 200 # rect_h
self.w = 600 # rect_w
self.centers = [70, 120, 170, 200, 280, 320, 400, 500, 510, 550]
# 向其中首位插入两个直径为0的圆作为哨兵
self.centers.insert(0, 0)
self.centers.insert(len(self.centers), self.w)
self.n = len(self.centers)
self.radius_my = [0] * len(self.centers)
self.img = np.ones((self.h, self.w, 3), dtype=np.uint8) * 100
self.draw_circles()
def draw_circles(self):
color = [0, 255, 255]
for i in range(1, self.n-1):
cv.circle(self.img, (self.centers[i], 100), 2, color, -1)
def esti_max_area(self):
half_h = self.h * 0.5 # 圆的极限半径是矩形短边的一半
drawed = [0] * self.n
drawed[0], drawed[self.n-1] = -1, -1 # 两个哨兵表示已经被占据
# A. 先依次计算每个圆的最大半径
max_rads = [0] * self.n
for i in range(self.n):
if drawed[i] != 0:
continue
# 1, 当前和前一个之间的圆心距, 减去前一个圆的最大半径,作为当前圆的最大半径
r1 = min(self.centers[i] - self.centers[i-1] - self.radius_my[i - 1], half_h)
# 2, 当前和后一个之间的圆心距作为当前圆的最大半径
r2 = min(self.centers[i+1] - self.centers[i] - self.radius_my[i + 1], half_h)
ri = min(r1, r2)
max_rads[i] = ri
# B. 对孤立的圆可以直接确定其半径
max_rad_idxs = np.argsort(max_rads)[::-1]
for i, idx in enumerate(max_rad_idxs):
if idx >= 1 and idx <= self.n-2 and drawed[idx-1] == 0 and drawed[idx+1] == 0:
# 当前的是最大半径,直接先占据,标记为第一次
drawed[idx] = 1
self.radius_my[idx] = int(max_rads[idx])
cv.circle(self.img, (self.centers[idx], 100), self.radius_my[idx], [255, 0, 0], 1)
pass
# C. 对剩余的圆进行填充
num_undo = sum([1 if di == 0 else 0 for di in drawed])
while num_undo > 0:
# 先比较得出最大的半径填充
max_rad = -1
max_rad_idx = -1
for i in range(1, self.n-1):
if drawed[i] != 0:
continue
r1 = min(self.centers[i] - self.centers[i - 1] - self.radius_my[i - 1], half_h)
r2 = min(self.centers[i + 1] - self.centers[i] - self.radius_my[i + 1], half_h)
ri = min(r1, r2)
if ri > max_rad:
max_rad = ri
max_rad_idx = i
# 当圆的最大半径为零时,表示已经全部遍历完成
if max_rad <= 0.:
break
drawed[max_rad_idx] = 2
self.radius_my[max_rad_idx] = max_rad
num_undo = sum([1 if di == 0 else 0 for di in drawed])
cv.circle(self.img, (self.centers[max_rad_idx], 100), self.radius_my[max_rad_idx], [0, 0, 255], 1)
cv.imshow("img", self.img)
cv.waitKey()
rads = self.radius_my[1:-1]
areas = [np.pi * r * r for r in rads]
sum_area = sum(areas)
print(f"max_area is {sum_area:.2f}")
return rads
if __name__ == '__main__':
max_circle = MaxCircleAreaInRect()
max_circle.esti_max_area()
结果
虽然解出来了,但还请大家多指教,指出更优的算法🙏!