Загрузка данных
"""
Задача:
Черепаха выполнила программу:
Повтори 2 [Вперёд 14 Налево 270 Назад 12 Направо 90]
Поднять хвост
Вперёд 9 Направо 90 Назад 7 Налево 90
Опустить хвост
Повтори 2 [Вперёд 13 Направо 90 Вперёд 6 Направо 90]
Найти, сколько точек с целочисленными координатами лежат внутри
пересечения фигур, ограниченных линиями (включая точки на самих линиях).
"""
from fractions import Fraction as F
# ---------------------------------------------------------------------------
# 1. Черепаха: т.к. все повороты кратны 90°, направление всегда совпадает
# с одной из осей — считаем точно, без ошибок округления float.
# ---------------------------------------------------------------------------
DIRS = {0: (F(1), F(0)), 90: (F(0), F(1)), 180: (F(-1), F(0)), 270: (F(0), F(-1))}
class Turtle:
def __init__(self):
self.x, self.y = F(0), F(0)
self.heading = 0 # 0, 90, 180 или 270
self.pen_down = True
self.path = [(self.x, self.y)] # точки текущей (нарисованной) ломаной
def _dir(self):
return DIRS[self.heading % 360]
def forward(self, d):
dx, dy = self._dir()
self.x += dx * d
self.y += dy * d
if self.pen_down:
self.path.append((self.x, self.y))
def backward(self, d):
self.forward(-d)
def left(self, a):
self.heading = (self.heading + a) % 360
def right(self, a):
self.heading = (self.heading - a) % 360
def pen_up(self):
self.pen_down = False
def pen_down_(self):
self.pen_down = True
self.path = [(self.x, self.y)] # начинаем новую фигуру
def run_program():
t = Turtle()
# --- Первая фигура ---
for _ in range(2):
t.forward(14)
t.left(270)
t.backward(12)
t.right(90)
shape1 = dedupe(t.path)
# --- Переход без рисования ---
t.pen_up()
t.forward(9)
t.right(90)
t.backward(7)
t.left(90)
t.pen_down_()
# --- Вторая фигура ---
for _ in range(2):
t.forward(13)
t.right(90)
t.forward(6)
t.right(90)
shape2 = dedupe(t.path)
return shape1, shape2
def dedupe(points):
"""Убираем повторяющиеся подряд точки (замыкание контура)."""
out = [points[0]]
for p in points[1:]:
if p != out[-1]:
out.append(p)
if out[0] == out[-1]:
out.pop()
return out
# ---------------------------------------------------------------------------
# 2. Пересечение двух выпуклых многоугольников: Sutherland–Hodgman clipping.
# Работает для любых выпуклых фигур, не только для прямоугольников.
# ---------------------------------------------------------------------------
def clip_polygon(subject, clip):
def inside(p, a, b):
# знак векторного произведения — с какой стороны от ребра (a->b) лежит p
return (b[0]-a[0])*(p[1]-a[1]) - (b[1]-a[1])*(p[0]-a[0]) >= 0
def intersect(p1, p2, a, b):
x1, y1 = p1; x2, y2 = p2
x3, y3 = a; x4, y4 = b
d = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4)
if d == 0:
return p2
t_ = ((x1-x3)*(y3-y4) - (y1-y3)*(x3-x4)) / d
return (x1 + t_*(x2-x1), y1 + t_*(y2-y1))
output = subject
n = len(clip)
for i in range(n):
a, b = clip[i], clip[(i+1) % n]
input_list = output
output = []
if not input_list:
break
for j in range(len(input_list)):
cur = input_list[j]
prev = input_list[j-1]
cur_in = inside(cur, a, b)
prev_in = inside(prev, a, b)
if cur_in:
if not prev_in:
output.append(intersect(prev, cur, a, b))
output.append(cur)
elif prev_in:
output.append(intersect(prev, cur, a, b))
return output
def ensure_ccw(poly):
area2 = sum(poly[i][0]*poly[(i+1) % len(poly)][1] - poly[(i+1) % len(poly)][0]*poly[i][1]
for i in range(len(poly)))
return poly if area2 > 0 else poly[::-1]
# ---------------------------------------------------------------------------
# 3. Подсчёт целых точек внутри многоугольника И на его границе.
# ---------------------------------------------------------------------------
def point_on_segment(p, a, b):
cross = (b[0]-a[0])*(p[1]-a[1]) - (b[1]-a[1])*(p[0]-a[0])
if cross != 0:
return False
return min(a[0], b[0]) <= p[0] <= max(a[0], b[0]) and \
min(a[1], b[1]) <= p[1] <= max(a[1], b[1])
def point_in_polygon(p, poly):
n = len(poly)
for i in range(n):
if point_on_segment(p, poly[i], poly[(i+1) % n]):
return True # на границе — считаем как "внутри пересечения"
# ray casting для строго внутренних точек
x, y = p
inside = False
for i in range(n):
x1, y1 = poly[i]
x2, y2 = poly[(i+1) % n]
if (y1 > y) != (y2 > y):
xinters = x1 + (y - y1) * (x2 - x1) / (y2 - y1)
if x < xinters:
inside = not inside
return inside
def count_lattice_points(poly):
xs = [p[0] for p in poly]
ys = [p[1] for p in poly]
xmin, xmax = int(min(xs)), int(max(xs))
ymin, ymax = int(min(ys)), int(max(ys))
total = 0
pts = []
for X in range(xmin, xmax+1):
for Y in range(ymin, ymax+1):
if point_in_polygon((F(X), F(Y)), poly):
total += 1
pts.append((X, Y))
return total, pts
# ---------------------------------------------------------------------------
# Основной запуск
# ---------------------------------------------------------------------------
if __name__ == "__main__":
shape1, shape2 = run_program()
print("Фигура 1 (вершины):", shape1)
print("Фигура 2 (вершины):", shape2)
shape1 = ensure_ccw(shape1)
shape2 = ensure_ccw(shape2)
intersection = clip_polygon(shape1, shape2)
print("\nПересечение (вершины):", intersection)
if intersection:
total, pts = count_lattice_points(intersection)
print(f"\nЦелочисленных точек внутри пересечения (включая границу): {total}")
print("Точки:", sorted(pts))
else:
print("Фигуры не пересекаются.")