comments | difficulty | edit_url | rating | source | tags | ||||||
---|---|---|---|---|---|---|---|---|---|---|---|
true |
困难 |
3773 |
第 408 场周赛 Q4 |
|
给你两个正整数 xCorner
和 yCorner
和一个二维整数数组 circles
,其中 circles[i] = [xi, yi, ri]
表示一个圆心在 (xi, yi)
半径为 ri
的圆。
坐标平面内有一个左下角在原点,右上角在 (xCorner, yCorner)
的矩形。你需要判断是否存在一条从左下角到右上角的路径满足:路径 完全 在矩形内部,不会 触碰或者经过 任何 圆的内部和边界,同时 只 在起点和终点接触到矩形。
如果存在这样的路径,请你返回 true
,否则返回 false
。
示例 1:
示例 2:
示例 3:
示例 4:
提示:
3 <= xCorner, yCorner <= 109
1 <= circles.length <= 1000
circles[i].length == 3
1 <= xi, yi, ri <= 109
根据题意,我们分情况讨论:
当 circles
中只有一个圆时:
- 如果起点
$(0, 0)$ 在圆内(包括边界),或者终点$(\textit{xCorner}, \textit{yCorner})$ 在圆内,那么无法满足“不触碰圆”的条件; - 如果圆与矩形的左侧或上侧有交点,且与矩形的右侧或下侧有交点,这种情况下,圆会阻断从矩形左下角到右上角的路径,也无法满足“不触碰圆”的条件。
当 circles
中有多个圆时:
- 与上述情况类似,如果起点或终点在圆内时,无法满足“不触碰圆”的条件。
- 如果有多个圆,多个圆之间可能在矩形内相交,合并形成更大的障碍区域。只要这个障碍区域与矩形的左侧或上侧有交点,且与矩形的右侧或下侧有交点,那么无法满足“不触碰圆”的条件。如果相交区域不在矩形内部,不能进行合并,因为相交的区域无法阻断矩形内部路径。另外,如果相交的区域有一部分在矩形内,有一部分在矩形外,这些圆都可以作为搜索的起点或终点,可以合并,也可以不合并。我们只要任选相交的其中一个点,如果这个点在矩形内,我们就可以将这些圆合并。
根据上述分析,我们遍历所有圆,对于当前遍历到的圆,如果起点或终点在圆内,我们直接返回 false
。否则,如果这个点没有被访问过,且这个圆与矩形的左侧或上侧有交点,我们就从这个圆开始进行深度优先搜索,搜索过程中,如果找到了一个圆,它与矩形的右侧或下侧有交点,说明圆形成的障碍区域阻断了从矩形左下角到右上角的路径,我们就返回 false
。
我们定义 true
,否则返回 false
。
函数
- 如果当前圆与矩形的右侧或下侧有交点,返回
true
; - 否则,我们将当前圆标记为已访问;
- 接下来,遍历其它所有圆,如果圆
$j$ 没被访问过,且圆$i$ 和圆$j$ 相交,且这两个圆的其中一个交点在矩形内,我们就继续从圆$j$ 开始进行深度优先搜索,如果找到了一个圆,它与矩形的右侧或下侧有交点,返回true
; - 如果没有找到这样的圆,返回
false
。
上面的过程中,我们需要在圆
我们还需要寻找两个圆的一个交点,我们取一个点
时间复杂度
class Solution:
def canReachCorner(
self, xCorner: int, yCorner: int, circles: List[List[int]]
) -> bool:
def in_circle(x: int, y: int, cx: int, cy: int, r: int) -> int:
return (x - cx) ** 2 + (y - cy) ** 2 <= r**2
def cross_left_top(cx: int, cy: int, r: int) -> bool:
a = abs(cx) <= r and 0 <= cy <= yCorner
b = abs(cy - yCorner) <= r and 0 <= cx <= xCorner
return a or b
def cross_right_bottom(cx: int, cy: int, r: int) -> bool:
a = abs(cx - xCorner) <= r and 0 <= cy <= yCorner
b = abs(cy) <= r and 0 <= cx <= xCorner
return a or b
def dfs(i: int) -> bool:
x1, y1, r1 = circles[i]
if cross_right_bottom(x1, y1, r1):
return True
vis[i] = True
for j, (x2, y2, r2) in enumerate(circles):
if vis[j] or not ((x1 - x2) ** 2 + (y1 - y2) ** 2 <= (r1 + r2) ** 2):
continue
if (
(x1 * r2 + x2 * r1 < (r1 + r2) * xCorner)
and (y1 * r2 + y2 * r1 < (r1 + r2) * yCorner)
and dfs(j)
):
return True
return False
vis = [False] * len(circles)
for i, (x, y, r) in enumerate(circles):
if in_circle(0, 0, x, y, r) or in_circle(xCorner, yCorner, x, y, r):
return False
if (not vis[i]) and cross_left_top(x, y, r) and dfs(i):
return False
return True
class Solution {
private int[][] circles;
private int xCorner, yCorner;
private boolean[] vis;
public boolean canReachCorner(int xCorner, int yCorner, int[][] circles) {
int n = circles.length;
this.circles = circles;
this.xCorner = xCorner;
this.yCorner = yCorner;
vis = new boolean[n];
for (int i = 0; i < n; ++i) {
var c = circles[i];
int x = c[0], y = c[1], r = c[2];
if (inCircle(0, 0, x, y, r) || inCircle(xCorner, yCorner, x, y, r)) {
return false;
}
if (!vis[i] && crossLeftTop(x, y, r) && dfs(i)) {
return false;
}
}
return true;
}
private boolean inCircle(long x, long y, long cx, long cy, long r) {
return (x - cx) * (x - cx) + (y - cy) * (y - cy) <= r * r;
}
private boolean crossLeftTop(long cx, long cy, long r) {
boolean a = Math.abs(cx) <= r && (cy >= 0 && cy <= yCorner);
boolean b = Math.abs(cy - yCorner) <= r && (cx >= 0 && cx <= xCorner);
return a || b;
}
private boolean crossRightBottom(long cx, long cy, long r) {
boolean a = Math.abs(cx - xCorner) <= r && (cy >= 0 && cy <= yCorner);
boolean b = Math.abs(cy) <= r && (cx >= 0 && cx <= xCorner);
return a || b;
}
private boolean dfs(int i) {
var c = circles[i];
long x1 = c[0], y1 = c[1], r1 = c[2];
if (crossRightBottom(x1, y1, r1)) {
return true;
}
vis[i] = true;
for (int j = 0; j < circles.length; ++j) {
var c2 = circles[j];
long x2 = c2[0], y2 = c2[1], r2 = c2[2];
if (vis[j]) {
continue;
}
if ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) > (r1 + r2) * (r1 + r2)) {
continue;
}
if (x1 * r2 + x2 * r1 < (r1 + r2) * xCorner && y1 * r2 + y2 * r1 < (r1 + r2) * yCorner
&& dfs(j)) {
return true;
}
}
return false;
}
}
class Solution {
public:
bool canReachCorner(int xCorner, int yCorner, vector<vector<int>>& circles) {
using ll = long long;
auto inCircle = [&](ll x, ll y, ll cx, ll cy, ll r) {
return (x - cx) * (x - cx) + (y - cy) * (y - cy) <= r * r;
};
auto crossLeftTop = [&](ll cx, ll cy, ll r) {
bool a = abs(cx) <= r && (cy >= 0 && cy <= yCorner);
bool b = abs(cy - yCorner) <= r && (cx >= 0 && cx <= xCorner);
return a || b;
};
auto crossRightBottom = [&](ll cx, ll cy, ll r) {
bool a = abs(cx - xCorner) <= r && (cy >= 0 && cy <= yCorner);
bool b = abs(cy) <= r && (cx >= 0 && cx <= xCorner);
return a || b;
};
int n = circles.size();
vector<bool> vis(n);
auto dfs = [&](this auto&& dfs, int i) -> bool {
auto c = circles[i];
ll x1 = c[0], y1 = c[1], r1 = c[2];
if (crossRightBottom(x1, y1, r1)) {
return true;
}
vis[i] = true;
for (int j = 0; j < n; ++j) {
if (vis[j]) {
continue;
}
auto c2 = circles[j];
ll x2 = c2[0], y2 = c2[1], r2 = c2[2];
if ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) > (r1 + r2) * (r1 + r2)) {
continue;
}
if (x1 * r2 + x2 * r1 < (r1 + r2) * xCorner && y1 * r2 + y2 * r1 < (r1 + r2) * yCorner
&& dfs(j)) {
return true;
}
}
return false;
};
for (int i = 0; i < n; ++i) {
auto c = circles[i];
ll x = c[0], y = c[1], r = c[2];
if (inCircle(0, 0, x, y, r) || inCircle(xCorner, yCorner, x, y, r)) {
return false;
}
if (!vis[i] && crossLeftTop(x, y, r) && dfs(i)) {
return false;
}
}
return true;
}
};
func canReachCorner(xCorner int, yCorner int, circles [][]int) bool {
inCircle := func(x, y, cx, cy, r int) bool {
dx, dy := x-cx, y-cy
return dx*dx+dy*dy <= r*r
}
crossLeftTop := func(cx, cy, r int) bool {
a := abs(cx) <= r && cy >= 0 && cy <= yCorner
b := abs(cy-yCorner) <= r && cx >= 0 && cx <= xCorner
return a || b
}
crossRightBottom := func(cx, cy, r int) bool {
a := abs(cx-xCorner) <= r && cy >= 0 && cy <= yCorner
b := abs(cy) <= r && cx >= 0 && cx <= xCorner
return a || b
}
vis := make([]bool, len(circles))
var dfs func(int) bool
dfs = func(i int) bool {
c := circles[i]
x1, y1, r1 := c[0], c[1], c[2]
if crossRightBottom(x1, y1, r1) {
return true
}
vis[i] = true
for j, c2 := range circles {
if vis[j] {
continue
}
x2, y2, r2 := c2[0], c2[1], c2[2]
if (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2) > (r1+r2)*(r1+r2) {
continue
}
if x1*r2+x2*r1 < (r1+r2)*xCorner && y1*r2+y2*r1 < (r1+r2)*yCorner && dfs(j) {
return true
}
}
return false
}
for i, c := range circles {
x, y, r := c[0], c[1], c[2]
if inCircle(0, 0, x, y, r) || inCircle(xCorner, yCorner, x, y, r) {
return false
}
if !vis[i] && crossLeftTop(x, y, r) && dfs(i) {
return false
}
}
return true
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
function canReachCorner(xCorner: number, yCorner: number, circles: number[][]): boolean {
const inCircle = (x: bigint, y: bigint, cx: bigint, cy: bigint, r: bigint): boolean => {
const dx = x - cx;
const dy = y - cy;
return dx * dx + dy * dy <= r * r;
};
const crossLeftTop = (cx: bigint, cy: bigint, r: bigint): boolean => {
const a = BigInt(Math.abs(Number(cx))) <= r && cy >= 0n && cy <= BigInt(yCorner);
const b =
BigInt(Math.abs(Number(cy - BigInt(yCorner)))) <= r &&
cx >= 0n &&
cx <= BigInt(xCorner);
return a || b;
};
const crossRightBottom = (cx: bigint, cy: bigint, r: bigint): boolean => {
const a =
BigInt(Math.abs(Number(cx - BigInt(xCorner)))) <= r &&
cy >= 0n &&
cy <= BigInt(yCorner);
const b = BigInt(Math.abs(Number(cy))) <= r && cx >= 0n && cx <= BigInt(xCorner);
return a || b;
};
const n = circles.length;
const vis: boolean[] = new Array(n).fill(false);
const dfs = (i: number): boolean => {
const [x1, y1, r1] = circles[i].map(BigInt);
if (crossRightBottom(x1, y1, r1)) {
return true;
}
vis[i] = true;
for (let j = 0; j < n; j++) {
if (vis[j]) continue;
const [x2, y2, r2] = circles[j].map(BigInt);
if ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) > (r1 + r2) * (r1 + r2)) {
continue;
}
if (
x1 * r2 + x2 * r1 < (r1 + r2) * BigInt(xCorner) &&
y1 * r2 + y2 * r1 < (r1 + r2) * BigInt(yCorner) &&
dfs(j)
) {
return true;
}
}
return false;
};
for (let i = 0; i < n; i++) {
const [x, y, r] = circles[i].map(BigInt);
if (inCircle(0n, 0n, x, y, r) || inCircle(BigInt(xCorner), BigInt(yCorner), x, y, r)) {
return false;
}
if (!vis[i] && crossLeftTop(x, y, r) && dfs(i)) {
return false;
}
}
return true;
}
impl Solution {
pub fn can_reach_corner(x_corner: i32, y_corner: i32, circles: Vec<Vec<i32>>) -> bool {
let n = circles.len();
let mut vis = vec![false; n];
let in_circle = |x: i64, y: i64, cx: i64, cy: i64, r: i64| -> bool {
(x - cx) * (x - cx) + (y - cy) * (y - cy) <= r * r
};
let cross_left_top = |cx: i64, cy: i64, r: i64| -> bool {
let a = cx.abs() <= r && (cy >= 0 && cy <= y_corner as i64);
let b = (cy - y_corner as i64).abs() <= r && (cx >= 0 && cx <= x_corner as i64);
a || b
};
let cross_right_bottom = |cx: i64, cy: i64, r: i64| -> bool {
let a = (cx - x_corner as i64).abs() <= r && (cy >= 0 && cy <= y_corner as i64);
let b = cy.abs() <= r && (cx >= 0 && cx <= x_corner as i64);
a || b
};
fn dfs(
circles: &Vec<Vec<i32>>,
vis: &mut Vec<bool>,
i: usize,
x_corner: i32,
y_corner: i32,
cross_right_bottom: &dyn Fn(i64, i64, i64) -> bool,
) -> bool {
let c = &circles[i];
let (x1, y1, r1) = (c[0] as i64, c[1] as i64, c[2] as i64);
if cross_right_bottom(x1, y1, r1) {
return true;
}
vis[i] = true;
for j in 0..circles.len() {
if vis[j] {
continue;
}
let c2 = &circles[j];
let (x2, y2, r2) = (c2[0] as i64, c2[1] as i64, c2[2] as i64);
if (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) > (r1 + r2) * (r1 + r2) {
continue;
}
if x1 * r2 + x2 * r1 < (r1 + r2) * x_corner as i64
&& y1 * r2 + y2 * r1 < (r1 + r2) * y_corner as i64
&& dfs(circles, vis, j, x_corner, y_corner, cross_right_bottom)
{
return true;
}
}
false
}
for i in 0..n {
let c = &circles[i];
let (x, y, r) = (c[0] as i64, c[1] as i64, c[2] as i64);
if in_circle(0, 0, x, y, r) || in_circle(x_corner as i64, y_corner as i64, x, y, r) {
return false;
}
if !vis[i]
&& cross_left_top(x, y, r)
&& dfs(
&circles,
&mut vis,
i,
x_corner,
y_corner,
&cross_right_bottom,
)
{
return false;
}
}
true
}
}