算法编程题目解答

算法编程题解答

朋友的题目

今天朋友遇到了一个算法题目,感觉很有意思,在这里稍微分享解答一下。但是我的答案效率并不很高,就当是抛砖引玉,希望有助于大家!也希望有兴趣的大神指点一下~

题目

如下图,有一个长为L,宽为W的矩形,L为矩形两个短边中点的连线。在L上依次分布n个点a1, a2, …, an(其中 0<= ai <= l)。
现在以这n个点为圆心画圆,要求所有的圆与圆、圆与矩形框之间不能相交,请给出每个点对应的圆的半径,是的所有圆的面积最大。
图1

思路

个人认为,最大圆的面积的关键在于,依次填充半径最大的圆。如下图所示,假设矩形框长160,宽100,那么下图一中圆的面积为
area1 = pi * (50^2 + 30^2) = 3400pi
图12.
图二中的圆的面积为area1 = pi * (40^2 + 40^2) = 3200pi
图11

答案

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()

结果

结果1
结果2
虽然解出来了,但还请大家多指教,指出更优的算法🙏!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值