mirror of
https://github.com/InternLM/InternBootcamp.git
synced 2026-04-29 17:35:14 +00:00
410 lines
12 KiB
Python
Executable file
410 lines
12 KiB
Python
Executable file
"""#
|
|
|
|
### 谜题描述
|
|
Firecrackers scare Nian the monster, but they're wayyyyy too noisy! Maybe fireworks make a nice complement.
|
|
|
|
Little Tommy is watching a firework show. As circular shapes spread across the sky, a splendid view unfolds on the night of Lunar New Year's eve.
|
|
|
|
A wonder strikes Tommy. How many regions are formed by the circles on the sky? We consider the sky as a flat plane. A region is a connected part of the plane with positive area, whose bound consists of parts of bounds of the circles and is a curve or several curves without self-intersections, and that does not contain any curve other than its boundaries. Note that exactly one of the regions extends infinitely.
|
|
|
|
Input
|
|
|
|
The first line of input contains one integer n (1 ≤ n ≤ 3), denoting the number of circles.
|
|
|
|
The following n lines each contains three space-separated integers x, y and r ( - 10 ≤ x, y ≤ 10, 1 ≤ r ≤ 10), describing a circle whose center is (x, y) and the radius is r. No two circles have the same x, y and r at the same time.
|
|
|
|
Output
|
|
|
|
Print a single integer — the number of regions on the plane.
|
|
|
|
Examples
|
|
|
|
Input
|
|
|
|
3
|
|
0 0 1
|
|
2 0 1
|
|
4 0 1
|
|
|
|
|
|
Output
|
|
|
|
4
|
|
|
|
|
|
Input
|
|
|
|
3
|
|
0 0 2
|
|
3 0 2
|
|
6 0 2
|
|
|
|
|
|
Output
|
|
|
|
6
|
|
|
|
|
|
Input
|
|
|
|
3
|
|
0 0 2
|
|
2 0 2
|
|
1 1 2
|
|
|
|
|
|
Output
|
|
|
|
8
|
|
|
|
Note
|
|
|
|
For the first example,
|
|
|
|
<image>
|
|
|
|
For the second example,
|
|
|
|
<image>
|
|
|
|
For the third example,
|
|
|
|
<image>
|
|
|
|
Here is a reference code to solve this task. You can use this to help you genereate cases or validate the solution.
|
|
```python
|
|
from math import sqrt
|
|
|
|
class vector:
|
|
def __init__(self, _x = 0, _y = 0):
|
|
self.x = _x
|
|
self.y = _y
|
|
def len(self):
|
|
return sqrt(self.x ** 2 + self.y ** 2)
|
|
def len_sq(self):
|
|
return self.x ** 2 + self.y ** 2
|
|
def __mul__(self, other):
|
|
if (type(self) == type(other)):
|
|
return self.x * other.x + self.y * other.y
|
|
return vector(self.x * other, self.y * other)
|
|
def __mod__(self, other):
|
|
return self.x * other.y - self.y * other.x
|
|
def normed(self):
|
|
length = self.len()
|
|
return vector(self.x / length, self.y / length)
|
|
def normate(self):
|
|
self = self.normed()
|
|
def __str__(self):
|
|
return \"(\" + str(self.x) + \", \" + str(self.y) + \")\"
|
|
def __add__(self, other):
|
|
return vector(self.x + other.x, self.y + other.y);
|
|
def __sub__(self, other):
|
|
return vector(self.x - other.x, self.y - other.y);
|
|
def __eq__(self, other):
|
|
return self.x == other.x and self.y == other.y
|
|
def rot(self):
|
|
return vector(self.y, -self.x)
|
|
|
|
class line:
|
|
def __init__(self, a = 0, b = 0, c = 0):
|
|
self.a = a
|
|
self.b = b
|
|
self.c = c
|
|
def intersect(self, other):
|
|
d = self.a * other.b - self.b * other.a
|
|
dx = self.c * other.b - self.b * other.c
|
|
dy = self.a * other.c - self.c * other.a
|
|
return vector(dx / d, dy / d)
|
|
def fake(self, other):
|
|
d = self.a * other.b - self.b * other.a
|
|
return d
|
|
def __str__(self):
|
|
return str(self.a) + \"*x + \" + str(self.b) + \"*y = \" + str(self.c)
|
|
|
|
def line_pt(A, B):
|
|
d = (A - B).rot()
|
|
return line(d.x, d.y, d * A)
|
|
|
|
class circle:
|
|
def __init__(self, O = vector(0, 0), r = 0):
|
|
self.O = O
|
|
self.r = r
|
|
def intersect(self, other):
|
|
O1 = self.O
|
|
O2 = other.O
|
|
r1 = self.r
|
|
r2 = other.r
|
|
if (O1 == O2):
|
|
return []
|
|
if ((O1 - O2).len_sq() > r1 ** 2 + r2 ** 2 + 2 * r1 * r2):
|
|
return []
|
|
rad_line = line(2 * (O2.x - O1.x), 2 * (O2.y - O1.y), r1 ** 2 - O1.len_sq() - r2 ** 2 + O2.len_sq())
|
|
central = line_pt(O1, O2)
|
|
M = rad_line.intersect(central)
|
|
if ((O1 - O2).len_sq() == r1 ** 2 + r2 ** 2 + 2 * r1 * r2):
|
|
return [M]
|
|
d = (O2 - O1).normed().rot()
|
|
if (r1 ** 2 - (O1 - M).len_sq() < 0):
|
|
return []
|
|
d = d * (sqrt(r1 ** 2 - (O1 - M).len_sq()))
|
|
return [M + d, M - d]
|
|
def fake(self, other):
|
|
O1 = self.O
|
|
O2 = other.O
|
|
r1 = self.r
|
|
r2 = other.r
|
|
if (O1 == O2):
|
|
return 1
|
|
if ((O1 - O2).len_sq() > r1 ** 2 + r2 ** 2 + 2 * r1 * r2):
|
|
return 1
|
|
rad_line = line(2 * (O2.x - O1.x), 2 * (O2.y - O1.y), r1 ** 2 - O1.len_sq() - r2 ** 2 + O2.len_sq())
|
|
central = line_pt(O1, O2)
|
|
return rad_line.fake(central)
|
|
n = input()
|
|
arr = []
|
|
m = 1
|
|
for i in range(n):
|
|
x, y, r = map(int, raw_input().split())
|
|
arr.append(circle(vector(x, y), r))
|
|
for i in range(n):
|
|
for j in range(i + 1, n):
|
|
m *= arr[i].fake(arr[j])
|
|
for i in range(n):
|
|
arr[i].O = arr[i].O * m
|
|
arr[i].r = arr[i].r * m
|
|
s = set()
|
|
V = 0
|
|
for i in range(n):
|
|
for j in range(i + 1, n):
|
|
tmp = arr[i].intersect(arr[j])
|
|
for e in tmp:
|
|
s.add((round(e.x, 6), round(e.y, 6)))
|
|
V += len(s)
|
|
E = 0
|
|
|
|
par = [i for i in range(n)]
|
|
|
|
def get_par(v):
|
|
if (par[v] != v):
|
|
par[v] = get_par(par[v])
|
|
return par[v]
|
|
def unite(v, u):
|
|
par[get_par(v)] = get_par(u)
|
|
for i in range(n):
|
|
s = set()
|
|
for j in range(n):
|
|
tmp = arr[i].intersect(arr[j])
|
|
if (len(tmp)):
|
|
unite(i, j)
|
|
for e in tmp:
|
|
s.add((round(e.x, ), round(e.y, )))
|
|
E += len(s)
|
|
print(E - V + 1 + len({get_par(i) for i in range(n)}))
|
|
```
|
|
|
|
|
|
请完成上述谜题的训练场环境类实现,包括所有必要的方法。
|
|
"""
|
|
|
|
from bootcamp import Basebootcamp
|
|
from math import sqrt, isclose
|
|
import random
|
|
import re
|
|
from bootcamp import Basebootcamp
|
|
|
|
class Eacolourfulprospectbootcamp(Basebootcamp):
|
|
class Vector:
|
|
def __init__(self, x=0, y=0):
|
|
self.x = x
|
|
self.y = y
|
|
def __add__(self, other):
|
|
return self.__class__(self.x + other.x, self.y + other.y)
|
|
def __sub__(self, other):
|
|
return self.__class__(self.x - other.x, self.y - other.y)
|
|
def __mul__(self, scalar):
|
|
return self.__class__(self.x * scalar, self.y * scalar)
|
|
def __mod__(self, other):
|
|
return self.x * other.y - self.y * other.x
|
|
def rot(self):
|
|
return self.__class__(self.y, -self.x)
|
|
def len_sq(self):
|
|
return self.x**2 + self.y**2
|
|
def normed(self):
|
|
length = sqrt(self.len_sq())
|
|
return self.__class__() if length == 0 else self * (1/length)
|
|
def __eq__(self, other):
|
|
return isclose(self.x, other.x, rel_tol=1e-9) and isclose(self.y, other.y, rel_tol=1e-9)
|
|
def __hash__(self):
|
|
return hash((round(self.x,9), round(self.y,9)))
|
|
|
|
class Line:
|
|
def __init__(self, a, b, c):
|
|
self.a = a
|
|
self.b = b
|
|
self.c = c
|
|
def intersect(self, other):
|
|
det = self.a * other.b - self.b * other.a
|
|
if isclose(det, 0, rel_tol=1e-9):
|
|
return None
|
|
x = (self.c * other.b - self.b * other.c) / det
|
|
y = (self.a * other.c - self.c * other.a) / det
|
|
return Eacolourfulprospectbootcamp.Vector(x, y)
|
|
def fake(self, other):
|
|
return self.a * other.b - self.b * other.a
|
|
|
|
@classmethod
|
|
def line_pt(cls, A, B):
|
|
dir_vec = (A - B).rot()
|
|
return cls.Line(dir_vec.x, dir_vec.y, dir_vec.x*A.x + dir_vec.y*A.y)
|
|
|
|
class Circle:
|
|
def __init__(self, O, r):
|
|
self.O = O
|
|
self.r = r
|
|
def intersect(self, other):
|
|
O1, O2 = self.O, other.O
|
|
r1, r2 = self.r, other.r
|
|
|
|
# Handle concentric circles
|
|
if O1 == O2:
|
|
return []
|
|
|
|
d_sq = (O1 - O2).len_sq()
|
|
sum_r = r1 + r2
|
|
dif_r = abs(r1 - r2)
|
|
|
|
if d_sq > sum_r**2 + 1e-9 or d_sq < dif_r**2 - 1e-9:
|
|
return []
|
|
|
|
line = Eacolourfulprospectbootcamp.Line(
|
|
2*(O2.x - O1.x),
|
|
2*(O2.y - O1.y),
|
|
r1**2 - O1.len_sq() - r2**2 + O2.len_sq()
|
|
)
|
|
central = Eacolourfulprospectbootcamp.line_pt(O1, O2)
|
|
M = line.intersect(central)
|
|
|
|
if not M:
|
|
return []
|
|
|
|
direction = (O2 - O1).normed().rot()
|
|
len_sq = r1**2 - (M - O1).len_sq()
|
|
|
|
if len_sq < -1e-9:
|
|
return []
|
|
if len_sq < 1e-9:
|
|
return [M]
|
|
|
|
d = sqrt(len_sq)
|
|
return [M + direction*d, M - direction*d]
|
|
|
|
def fake(self, other):
|
|
if self.O == other.O:
|
|
return 1
|
|
return Eacolourfulprospectbootcamp.line_pt(self.O, other.O).fake(
|
|
Eacolourfulprospectbootcamp.Line(
|
|
2*(other.O.x - self.O.x),
|
|
2*(other.O.y - self.O.y),
|
|
self.r**2 - other.r**2 + (other.O - self.O).len_sq()
|
|
)
|
|
)
|
|
|
|
def __init__(self, **params):
|
|
self.min_n = params.get('min_n', 1)
|
|
self.max_n = params.get('max_n', 3)
|
|
self.min_x = params.get('min_x', -10)
|
|
self.max_x = params.get('max_x', 10)
|
|
self.min_y = params.get('min_y', -10)
|
|
self.max_y = params.get('max_y', 10)
|
|
self.min_r = params.get('min_r', 1)
|
|
self.max_r = params.get('max_r', 10)
|
|
|
|
def case_generator(self):
|
|
MAX_ATTEMPTS = 100
|
|
for _ in range(MAX_ATTEMPTS):
|
|
try:
|
|
n = random.randint(self.min_n, self.max_n)
|
|
circles = []
|
|
for _ in range(n):
|
|
x = random.randint(self.min_x, self.max_x)
|
|
y = random.randint(self.min_y, self.max_y)
|
|
r = random.randint(self.min_r, self.max_r)
|
|
circles.append({'x': x, 'y': y, 'r': r})
|
|
|
|
# Validate case constraints
|
|
circles = [self.Circle(self.Vector(c['x'], c['y']), c['r']) for c in circles]
|
|
m = 1
|
|
for i in range(n):
|
|
for j in range(i+1, n):
|
|
if circles[i].O == circles[j].O and circles[i].r == circles[j].r:
|
|
raise ValueError("Duplicate circles")
|
|
m *= circles[i].fake(circles[j])
|
|
if m == 0:
|
|
raise ValueError("Invalid configuration")
|
|
|
|
return {
|
|
'n': n,
|
|
'circles': [
|
|
{'x': c.O.x, 'y': c.O.y, 'r': c.r}
|
|
for c in circles
|
|
]
|
|
}
|
|
except (ValueError, ZeroDivisionError):
|
|
continue
|
|
raise RuntimeError(f"Failed to generate valid case after {MAX_ATTEMPTS} attempts")
|
|
|
|
@staticmethod
|
|
def prompt_func(question_case):
|
|
circles = question_case['circles']
|
|
circles_str = "\n".join(f"{c['x']} {c['y']} {c['r']}" for c in circles)
|
|
return (
|
|
"Calculate regions formed by intersecting circles.\n"
|
|
f"Input:\n{question_case['n']}\n{circles_str}\n"
|
|
"Output format: [answer]INTEGER[/answer]"
|
|
)
|
|
|
|
@staticmethod
|
|
def extract_output(output):
|
|
match = re.search(r'\[answer\]\s*(\d+)\s*\[/answer\]', output, re.IGNORECASE)
|
|
return int(match.group(1)) if match else None
|
|
|
|
@classmethod
|
|
def _verify_correction(cls, solution, identity):
|
|
try:
|
|
return solution == cls._compute_regions(identity)
|
|
except:
|
|
return False
|
|
|
|
@classmethod
|
|
def _compute_regions(cls, identity):
|
|
circles = [
|
|
cls.Circle(
|
|
cls.Vector(c['x'], c['y']),
|
|
c['r']
|
|
) for c in identity['circles']
|
|
]
|
|
n = identity['n']
|
|
|
|
# Calculate intersection graph
|
|
parent = list(range(n))
|
|
def find(u):
|
|
while parent[u] != u:
|
|
parent[u] = parent[parent[u]]
|
|
u = parent[u]
|
|
return u
|
|
def union(u, v):
|
|
parent[find(u)] = find(v)
|
|
|
|
vertices = set()
|
|
edges = 0
|
|
|
|
for i in range(n):
|
|
for j in range(i+1, n):
|
|
points = circles[i].intersect(circles[j])
|
|
if points:
|
|
union(i, j)
|
|
edges += len(points)
|
|
vertices.update(points)
|
|
|
|
components = len({find(i) for i in range(n)})
|
|
return edges - len(vertices) + components + 1
|