求过原点且与所有圆相交的最少直线数的O(n.log(n))贪心算法
嘿,这个几何转贪心的问题我刚好有思路,咱们一步步来拆解!
问题转化:从几何到区间覆盖
首先得把“过原点的直线与圆相交”这个几何条件,转化成更容易处理的数学模型:
对于每个圆 ( c_i(x_i,y_i,r_i) ),计算原点到圆心的距离 ( d_i = \sqrt{x_i^2 + y_i^2} ):
- 如果 ( d_i \leq r_i ):原点在圆内(或圆上),任何过原点的直线都会和这个圆相交,直接把这类圆从待处理集合里剔除就行
- 如果 ( d_i > r_i ):过原点的直线和圆相交的充要条件是,圆心到直线的距离 ≤ ( r_i )。把直线用方向角 ( \theta ) 表示(从x轴正方向逆时针转的角度,范围统一为 ( [0, 2\pi) )),这个条件可以转化为:( \theta ) 必须落在区间 ( [\phi_i - \alpha_i, \phi_i + \alpha_i] ) 内,其中
- ( \phi_i = \text{arctan2}(y_i, x_i) )(圆心的极角)
- ( \alpha_i = \arcsin(r_i/d_i) )(直线允许的角度偏移量)
这里要注意:如果计算出的区间跨了 ( 2\pi )(比如 ( [3\pi/2, \pi/2] )),需要把它拆成两个不跨周期的区间:( [3\pi/2, 2\pi) ) 和 ( [0, \pi/2] ),同时把所有区间复制一份并加上 ( 2\pi )(用来处理跨周期的覆盖逻辑)。
到这里,问题就完全变成了经典的区间点覆盖问题:用最少的点(每个点对应一条直线的角度 ( \theta ))覆盖所有上述区间。而这个问题的最优贪心算法刚好满足 ( O(n\log n) ) 的时间复杂度!
贪心算法设计(最优且符合复杂度要求)
这个贪心策略的核心逻辑是:每次选能覆盖最多未覆盖区间的点,数学上已经证明,选择当前未覆盖区间中右端点最小的那个区间的右端点,就能得到最优解。具体步骤如下:
- 预处理所有圆:
- 对每个圆计算 ( d_i ),剔除 ( d_i \leq r_i ) 的圆
- 对剩下的圆计算对应的角度区间,处理跨周期的区间并拆分,同时复制所有区间并加上 ( 2\pi )
- 排序区间:
- 把所有有效区间按右端点从小到大排序,这一步的时间复杂度是 ( O(n\log n) ),也是整个算法的时间瓶颈
- 贪心选择直线:
- 初始化直线数量 ( count = 0 ),当前覆盖的最右端角度 ( current_end = -1 )(表示初始未覆盖)
- 遍历排序后的每个区间:
- 如果当前区间的左端点 > ( current_end ) 且 ( current_end < 2\pi ):说明这个区间还没被覆盖,需要新增一条直线,角度选这个区间的右端点,同时更新 ( current_end ) 为这个右端点,( count += 1 )
关于你提到的“构造3条直线”的补充
你说的遍历每个圆构造3条直线,可能是想尝试圆的切线或者其他关键角度?其实本质上,最优解里的每条直线对应的角度,一定是某个区间的右端点(也就是某个圆的过原点的切线角度,每个圆最多两条切线),所以不需要构造额外的第三条直线。把问题转化为区间覆盖后,用上面的贪心策略就能直接得到最少直线数,而且是严格最优的。
内容的提问来源于stack exchange,提问作者BolbazarMarme
相关产品推荐
相关产品推荐

