Google – Find a Path Among Circles
有一个通道,在y坐标0到1之间:
y = 1 —————————–
y = 0 —————————–
然后input一堆Circles,每个Circles有三个值,x, y, r, 分别表示圆心和半径。问题是在这条通道里是否能找出一条路径,可以从x的负无穷到正无穷。
路径不一定要直线,可以随便弯弯曲曲,但是如果有一个或者多个circle完全block了通道,就没有路径。
[Mistake]
一开始以为就是道Merge Interval的题目,合并每个圆的纵向interval, [y – r, y + r],一旦发现超过[0, 1]范围就返回false.
但是后来发现不是这样的:
比如一个圆在x = 0处,纵向interval为[0.5, 1.5],
另一个圆在x = 10000处,反正就是离的非常远,然后纵向interval为[-0.5, 0.9]
如果merge的话,肯定是超过[0, 1]范围,但是依然可以找出一条满足要求的路径。
所以这道题merge interval是有条件的,只有当两个圆相交或者相切,才能merge它们的纵向interval。
[Solution]
其实说白了就是cluster circles。只要有一个cluster的纵向interval超过范围了,就返回false。
Merge Intervals的变形,只有相交或者相切才merge,需要O( n2 ) time,因为每处理一个circle,都要和之前处理过的所有circle判断是否相交。
注意不能通过单纯通过sort x来判断是否相交,sort后第i个圆甚至可能出现和第i – 2个圆相交,但是和i – 1的圆没有半毛钱关系的情况。所以暂时没有想到比quadratic time更好的解法。
另外相交相切的条件:
Read full article from Google – Find a Path Among Circles
有一个通道,在y坐标0到1之间:
y = 1 —————————–
y = 0 —————————–
然后input一堆Circles,每个Circles有三个值,x, y, r, 分别表示圆心和半径。问题是在这条通道里是否能找出一条路径,可以从x的负无穷到正无穷。
路径不一定要直线,可以随便弯弯曲曲,但是如果有一个或者多个circle完全block了通道,就没有路径。
[Mistake]
一开始以为就是道Merge Interval的题目,合并每个圆的纵向interval, [y – r, y + r],一旦发现超过[0, 1]范围就返回false.
但是后来发现不是这样的:
比如一个圆在x = 0处,纵向interval为[0.5, 1.5],
另一个圆在x = 10000处,反正就是离的非常远,然后纵向interval为[-0.5, 0.9]
如果merge的话,肯定是超过[0, 1]范围,但是依然可以找出一条满足要求的路径。
所以这道题merge interval是有条件的,只有当两个圆相交或者相切,才能merge它们的纵向interval。
[Solution]
其实说白了就是cluster circles。只要有一个cluster的纵向interval超过范围了,就返回false。
Merge Intervals的变形,只有相交或者相切才merge,需要O( n2 ) time,因为每处理一个circle,都要和之前处理过的所有circle判断是否相交。
注意不能通过单纯通过sort x来判断是否相交,sort后第i个圆甚至可能出现和第i – 2个圆相交,但是和i – 1的圆没有半毛钱关系的情况。所以暂时没有想到比quadratic time更好的解法。
另外相交相切的条件:
圆心距离 < r1 + r2 相交
圆心距离 = r1 + r2 相切
圆心距离 > r1 + r2 相离
class
Circle {
double
x;
double
y;
double
r;
Circle(
double
x,
double
y,
double
r) {
this
.x = x;
this
.y = y;
this
.r = r;
}
}
class
Solution {
public
boolean
hasPath(Circle[] circles) {
if
(circles ==
null
|| circles.length ==
0
) {
return
true
;
}
Map<Circle, Interval> map =
new
HashMap<>();
map.put(circles[
0
],
new
Interval(circles[
0
].y - circles[
0
].r, circles[
0
].y + circles[
0
].r));
for
(
int
i =
1
; i < circles.length; i++) {
boolean
hasIntersection =
false
;
Interval newInterval =
new
Interval(circles[i].y - circles[i].r, circles[i].y + circles[i].r);
for
(
int
j =
0
; j < i; j++) {
if
(isIntersect(circles[j], circles[i])) {
merge(map.get(circles[j]), newInterval);
if
(map.get(circles[j]).s <=
0
&& map.get(circles[j]).e >=
1
) {
return
false
;
}
map.put(circles[i], map.get(circles[j]));
hasIntersection =
true
;
}
}
// maybe first get all intersect intervals and merge them at same time
// after for loop, update all intervals to the merged interval
if
(!hasIntersection) {
map.put(circles[i], newInterval);
}
}
return
true
;
}
private
boolean
isIntersect(Circle a, Circle b) {
double
x = Math.abs(a.x - b.x);
double
y = Math.abs(a.y - b.y);
double
len = Math.sqrt(x * x + y * y);
return
len <= a.r + b.r;
}
// If intersect, there must be an overlap
private
void
merge(Interval interval, Interval newInterval) {
interval.s = Math.min(interval.s, newInterval.s);
interval.e = Math.max(interval.e, newInterval.e);
}
private
class
Interval {
double
s;
double
e;
Interval(
double
s,
double
e) {
this
.s = s;
this
.e = e;
}
}
}