题目描述
【leetcode】5361. 圆和矩形是否有重叠( Circle and Rectangle Overlapping )
给你一个以 (radius, x_center, y_center) 表示的圆和一个与坐标轴平行的矩形 (x1, y1, x2, y2),其中 (x1, y1) 是矩形左下角的坐标,(x2, y2) 是右上角的坐标。
如果圆和矩形有重叠的部分,请你返回 True ,否则返回 False 。
换句话说,请你检测是否 存在 点 (xi, yi) ,它既在圆上也在矩形上(两者都包括点落在边界上的情况)。
示例 1:
输入:radius = 1, x_center = 0, y_center = 0, x1 = 1, y1 = -1, x2 = 3, y2 = 1
输出:true
解释:圆和矩形有公共点 (1,0)
示例 2:
输入:radius = 1, x_center = 0, y_center = 0, x1 = -1, y1 = 0, x2 = 0, y2 = 1
输出:true
示例 3:
输入:radius = 1, x_center = 1, y_center = 1, x1 = -3, y1 = -3, x2 = 3, y2 = 3
输出:true
示例 4:
输入:radius = 1, x_center = 1, y_center = 1, x1 = 1, y1 = -3, x2 = 2, y2 = -1
输出:false
提示:
- 1 <= radius <= 2000
- -10^4 <= x_center, y_center, x1, y1, x2, y2 <= 10^4
- x1 < x2
- y1 < y2
第一次解答
思路
因为坐标是整形,所以可以暴力法。遍历正方形区域每个像素,判断是否在圆内。
代码:
class Solution {
public:
bool checkOverlap(int radius, int x_center, int y_center, int x1, int y1, int x2, int y2) {
long r2 = radius*radius;
for(int y=y1; y<=y2; y++){
for(int x=x1; x<=x2; x++){
int dx = x-x_center;
int dy = y-y_center;
long d = dx*dx + dy*dy;
if(d <= r2)
return true;
}
}
return false;
}
};
结果:
第二次解答
思路
看了一种比较普适的方法,应该能用于大多数矩形区域相交的情况。
区域划分法。把空间划分为9个区域,圆在不同区域采取不同的策略。具体可以参考这里
划分区域如下图所示:
圆和矩形相交情况可归为如下三种:
代码:
// 思路:
// 数学思路不是每一题都适用,因此这里尝试一下看似普适的区域划分方法
class Solution {
public:
bool checkOverlap(int radius, int x_center, int y_center, int x1, int y1, int x2, int y2) {
// 圆心在矩形内情况
if(x1 <= x_center && x_center <= x2 && y1 <= y_center && y_center <= y2)
return true;
// 圆心在矩形上下左右区域
// 上
if(x1 <= x_center && x_center <= x2 && y_center >= y2 && y_center-y2 <= radius)
return true;
// 下
if(x1 <= x_center && x_center <= x2 && y_center <= y1 && y1 - y_center <= radius)
return true;
// 左
if(y1 <= y_center && y_center <= y2 && x_center <= x1 && x1-x_center <= radius)
return true;
// 右
if(y1 <= y_center && y_center <= y2 && x_center >= x2 && x_center-x2 <= radius)
return true;
// 圆心在矩形斜对角线上
long r2 = radius*radius;
if(abs(x1-x_center)*abs(x1-x_center) + abs(y2-y_center)*abs(y2-y_center) <= r2)
return true;
if(abs(x1-x_center)*abs(x1-x_center) + abs(y1-y_center)*abs(y1-y_center) <= r2)
return true;
if(abs(x2-x_center)*abs(x2-x_center) + abs(y1-y_center)*abs(y1-y_center) <= r2)
return true;
if(abs(x2-x_center)*abs(x2-x_center) + abs(y2-y_center)*abs(y2-y_center) <= r2)
return true;
return false;
}
};
结果:
第三次解答
思路
周赛中国区第一wifiii
的解法。
该解法可以从区域划分法的角度去理解。
圆和矩形相交情况可归为如下三种情况。图中,图1可用圆心是否在矩形内判断相交情况,图2可用矩形四个角点是否在圆内判断相交情况,但是图3比较麻烦,没有对应的点带入来判断相交情况,而wifiii
的算法简洁的实现了第三种情况的判断。
三种:
代码:
// 思路:
// 周赛冠军的解法,用区域划分来理解的话,它的方法是简洁的实现了当圆心在矩形外的上下左右区域的相交判断
class Solution {
public:
bool checkOverlap(int radius, int x_center, int y_center, int x1, int y1, int x2, int y2) {
auto incircle = [&](int x, int y){
return (x-x_center)*(x-x_center) + (y-y_center)*(y-y_center) <= radius*radius;};
auto insqure = [&](int x, int y){
return x1 <= x && x <= x2 && y1 <= y && y <= y2;};
// 这里组合有9个坐标,其中圆心和四个角点,这五个坐标可以判断图1和图2的相交情况。
// 剩下4个坐标用于判断图3那种相交情况
vector<int> vx{
x_center, x1, x2}, vy{
y_center, y1, y2};
bool is_ok = false;
for(auto & xx : vx)
for(auto & yy : vy)
if(incircle(xx, yy) && insqure(xx, yy))
is_ok = true;
return is_ok;
}
};
结果:
第四次解答
思路
数学法。参考知乎这里的方法。
注意,这种方法理论上最好,但是实际用的过程中产生了小数,所以有一定的误差。过程如下:
先把圆心映射到第一象限:
然后判断向量u的大小与半径的大小,得到相交情况:
代码:
// 思路:
// 用[知乎这里提到的方法](https://www.zhihu.com/question/24251545/answer/27184960)
class Solution {
public:
bool checkOverlap(int radius, int x_center, int y_center, int x1, int y1, int x2, int y2) {
// 向量cp转到第一象限
double cpx = abs(x_center-(x1+x2)/2.0);
double cpy = abs(y_center-(y1+y2)/2.0);
double hx = x2 - (x1+x2)/2.0;
double hy = y2 - (y1+y2)/2.0;
double ux = max(cpx-hx, 0.0);
double uy = max(cpy-hy, 0.0);
return ux*ux + uy*uy <= radius*radius;
}
};
结果: