Что проверяет задание
Исполнитель рисует на клетчатой плоскости: идёт вперёд, поворачивает, иногда поднимает и опускает хвост. Нужно понять, какая фигура получится, и посчитать клетки, точки или узлы по заданному правилу.
Определения
Исполнитель «Черепаха», чертёжник с положением и направлением. Он помнит координаты и куда смотрит.
Хвост опущен, движение оставляет след. Хвост поднят, перемещение без линии.
Клетчатая плоскость, плоскость, размеченная на квадраты со стороной 1. У клеток есть внутренность, стороны и узлы (точки пересечения линий сетки).
Узел, точка с целыми координатами, если шаг исполнителя согласован с сеткой.
Заливка, область, ограниченная следом. Вопрос может требовать клетки внутри контура, клетки контура или то и другое.
Самопересечение, след проходит по уже нарисованному участку. Тогда «внутри» нужно определять по фактической границе, а не по ожидаемому многоугольнику.
Поворот, смена направления. Поворот на 90° на клетчатой сетке меняет ось движения на перпендикулярную.
Цикл повторений, один и тот же набор команд выполняется несколько раз. Фигура при этом часто становится правильным многоугольником или «звездой».
Что именно считают
Формулировки различаются, и путать их нельзя:
- клетки, полностью лежащие внутри фигуры;
- клетки, через которые прошла линия;
- узлы, лежащие строго внутри;
- узлы на границе;
- клетки, которые исполнитель посетил при опущенном хвосте.
«Внутри» для самопересекающейся ломаной неоднозначно, пока не зафиксировано правило обхода. На экзамене обычно имеют в виду область, которую ограничивает замкнутый контур после всех команд.
Общий алгоритм решения
- Выписать начальную точку и начальное направление.
- Пройти программу шаг за шагом, отмечая только те перемещения, где хвост опущен.
- Получить геометрическую фигуру: отрезки, многоугольник, набор квадратов.
- Уточнить по условию, что считается ответом: внутренность, граница, узлы или клетки следа.
- Для регулярной фигуры (квадрат, прямоугольник, правильный многоугольник на сетке) посчитать по формуле площади и границы.
- Для составной фигуры разбить на прямоугольники и прямоугольные треугольники, не забывая вычесть перекрытия.
Программа построчно
Задание 6 удобнее всего решать так: нарисовать фигуру программой на Python точно так же, как её рисует Черепаха, наложить на рисунок сетку точек с целыми координатами и посчитать нужные точки. В Python для этого есть модуль turtle - та же черепаха, только на экране компьютера.
Разберём программу на алгоритме:
Повтори 2 [Вперёд 7 Направо 90 Вперёд 7 Направо 90] Поднять хвост Вперёд 4 Направо 90 Вперёд 5 Налево 90 Опустить хвост Повтори 2 [Вперёд 6 Направо 90 Вперёд 7 Направо 90]
from turtle import *
k = 25
tracer(0)
ht()
seth(90)
for _ in range(2):
fd(7 * k)
rt(90)
fd(7 * k)
rt(90)
up()
fd(4 * k)
rt(90)
fd(5 * k)
lt(90)
down()
for _ in range(2):
fd(6 * k)
rt(90)
fd(7 * k)
rt(90)
up()
for x in range(-1, 14):
for y in range(-1, 12):
goto(x * k, y * k)
dot(4, 'red')
done()
Программа состоит из трёх частей: начало (настройка черепахи), алгоритм из условия (каждая команда - своя строка) и сетка точек.
Начало программы
from turtle import * - подключаем модуль turtle целиком. Звёздочка значит «взять все команды модуля», поэтому дальше можно писать просто fd(…), а не turtle.fd(…).
tracer(0) - выключает анимацию: черепаха рисует всё мгновенно. Без этой строки большой рисунок может строиться минуту.
ht() - сокращение от hide turtle: прячет значок черепахи, чтобы он не закрывал точки.
seth(90) - сокращение от set heading: повернуть голову черепахи в направлении 90°. В Python-черепахе направление 0° - вправо (вдоль оси x), 90° - вверх. В задании 6 Черепаха в начале смотрит «вдоль положительного направления оси ординат», то есть вверх - поэтому seth(90). Если эту строку забыть, весь рисунок повернётся на 90° - количество точек останется тем же, но рисунок будет не похож на задуманный, и легко запутаться.
Масштаб k
k = 25 - масштаб: сколько пикселей экрана приходится на одну единицу Черепахи. Шаг «Вперёд 7» - это 7 единиц, а 7 пикселей на экране - крошечная чёрточка, на которой не разглядеть точки. Поэтому каждую длину умножаем на k: fd(7 * k) - это 7 · 25 = 175 пикселей.
Как выбирать k: чем больше фигура, тем меньше масштаб, чтобы рисунок поместился в окно. Для фигур размером до 10-12 единиц подходит 30-40, для фигур в 30-40 единиц - 8-15. Важно, чтобы все длины и сетка умножались на одно и то же k.
Команды черепахи в Python
Каждая команда из условия переводится в одну строку:
- Вперёд n →
fd(n * k)(forward) - пройти n единиц в ту сторону, куда смотрит голова. Если хвост опущен, остаётся линия. - Назад n →
bk(n * k)(back) - пройти n единиц спиной вперёд: голова не поворачивается, черепаха просто пятится. - Направо m →
rt(m)(right) - повернуть голову на m градусов по часовой стрелке. Угол наkне умножаем - это не длина. - Налево m →
lt(m)(left) - повернуть голову на m градусов против часовой стрелки.
Частая ошибка - перепутать rt и lt: фигура получится зеркальной, и при двух фигурах они могут перестать пересекаться.
Повтори k - цикл for
Запись Повтори k [Команда1 Команда2 …] - это цикл:
for _ in range(2):
fd(7 * k)
rt(90)
fd(7 * k)
rt(90)
range(2)выдаёт числа 0 и 1 - ровно 2 повторения. Число в скобкахrange- это k из «Повтори k»._- имя переменной цикла. Номер повторения нам не нужен, поэтому вместо имени ставим подчёркивание - так принято в Python.- Команды из квадратных скобок пишутся с отступом (4 пробела) под строкой
for- так Python понимает, что они повторяются. Команды после «]» пишутся снова без отступа: они выполняются один раз, после цикла.
Повтори 2 [Вперёд 7 Направо 90 Вперёд 7 Направо 90] рисует прямоугольник 7 × 7: за один проход - две стороны, за два прохода - все четыре.
Поднять и опустить хвост
- Поднять хвост →
up()- дальше черепаха двигается без следа. Так она переходит к началу второй фигуры, не рисуя лишнюю линию. - Опустить хвост →
down()- снова рисует.
Пока хвост поднят, команды fd, bk, rt, lt работают как обычно: черепаха меняет положение и направление, просто не оставляет линию. Поэтому переход между фигурами нужно повторить полностью, со всеми поворотами - от него зависит, где окажется вторая фигура.
Сетка точек
up()
for x in range(-1, 14):
for y in range(-1, 12):
goto(x * k, y * k)
dot(4, 'red')
up()- поднимаем хвост, чтобы при переходах от точки к точке не рисовались линии.- Два вложенных цикла перебирают все пары целых координат: для каждого x - все y.
range(-1, 14)даёт x от −1 до 13 (правая граница не входит). goto(x * k, y * k)- перенести черепаху в точку с координатами (x; y). Координаты умножаем на тот же масштабk, что и длины, иначе точки не совпадут с рисунком.dot(4, 'red')- поставить красную точку диаметром 4 пикселя.
Границы range берут по габаритам фигуры с запасом в одну клетку: самая левая и самая правая координата рисунка, самая нижняя и самая верхняя. Если фигура уходит в отрицательные координаты (например, после команды «Назад»), сетка тоже должна туда заходить.
done() - последняя строка: оставляет окно с рисунком открытым, пока его не закроют. Без неё окно закроется сразу после рисования.
Как считать точки
Запускаем программу и считаем красные точки по правилу из вопроса. Формулировки нужно читать очень внимательно:
- «строго внутри», «точки на линии учитывать не следует» - считаем только точки внутри фигуры; точки, через которые проходит линия, не считаем;
- «включая точки на линиях» - считаем и внутренние точки, и точки на линии;
- «на линии» - считаем только точки, через которые проходит линия; если линия проходит через точку дважды, её всё равно считаем один раз;
- «хотя бы одной из фигур» (объединение) - точка подходит, если она внутри любой из фигур; точку в общей части считаем один раз;
- «каждой из фигур» (пересечение) - точка подходит, только если она внутри обеих фигур;
- «ровно одной из фигур» - точка подходит, если она внутри одной фигуры, но не внутри другой.
Точку, которая лежит точно на линии, на рисунке видно: красная точка стоит на чёрной линии. Считать удобно построчно - по горизонтальным рядам точек снизу вверх.
Пересечение и объединение прямоугольников
Если обе фигуры - прямоугольники со сторонами вдоль осей, точки можно посчитать без рисунка.
Сколько точек в одном прямоугольнике. Пусть прямоугольник идёт от x₁ до x₂ и от y₁ до y₂, ширина w = x₂ − x₁, высота h = y₂ − y₁.
- С границей: подходят все x от x₁ до x₂ включительно - их w + 1 штука, и все y - их h + 1. Всего (w + 1) · (h + 1). Прямоугольник 7 × 7 содержит 8 · 8 = 64 точки.
- Строго внутри: x строго между x₁ и x₂ - их w − 1, y - h − 1. Всего (w − 1) · (h − 1).
Пересечение двух прямоугольников - тоже прямоугольник (если они пересекаются): от большего из левых краёв до меньшего из правых, от большего из нижних до меньшего из верхних. Его точки считаем той же формулой.
Объединение: точки₁ + точки₂ − точки_пересечения. Общие точки попали и в первое, и во второе слагаемое - то есть посчитаны дважды, поэтому один раз их вычитаем.
Ровно одна фигура: точки₁ + точки₂ − 2 · точки_пересечения - общие точки не подходят вовсе, поэтому вычитаем их из обоих слагаемых.
Пример: прямоугольники 7 × 7 (64 точки с границей) и 7 × 6 (56 точек), их пересечение содержит 12 точек. Объединение: 64 + 56 − 12 = 108.
Площадь фигуры
Площадь - это число единичных клеток внутри фигуры.
- Прямоугольник a × b: площадь a · b. Прямоугольник 11 × 10 - это 10 полос по 11 клеток, всего 110.
- Ступенчатая фигура (все стороны вдоль осей): разбиваем на прямоугольники и складываем их площади. На рисунке с сеткой удобно считать клетки построчно.
- Правильный многоугольник или фигура с наклонными сторонами: складываем площади треугольников или считаем по координатам вершин (формула площади Гаусса). Если в ответе просят целую часть площади, дробную часть отбрасываем, а не округляем: 64,95 → 64.
Подсчёт точек без рисунка
Для простых фигур, заданных неравенствами (квадрат 0 ≤ x ≤ 5, 0 ≤ y ≤ 5), рисовать ничего не нужно - достаточно перебрать все целые точки:
count = 0
for x in range(0, 6):
for y in range(0, 6):
count += 1
print(count)
count = 0- счётчик подходящих точек.- Вложенные циклы перебирают все x от 0 до 5 и все y от 0 до 5. Правая граница
rangeна единицу больше, потому что она не входит, а 5 входит («включая границу»). count += 1- каждая пара (x; y) лежит в квадрате, поэтому счётчик увеличиваем для каждой. Если бы граница не входила, перебирали быrange(1, 5).
Для фигуры с наклонными сторонами в цикл добавляют условие if с уравнениями сторон: точка подходит, если лежит по нужную сторону от каждой стороны фигуры. Для треугольника со стороной 10 «строго внутри»: if x / 3 ** 0.5 < y < -x / 3 ** 0.5 + 10: - строгие знаки < отсекают точки на сторонах.
Автоподсчёт
Точки можно посчитать и программой, не глядя на рисунок: залить фигуру цветом и проверить цвет холста в каждой целой точке.
from turtle import *
k = 50
speed(0)
ht()
fillcolor("red")
seth(90)
begin_fill()
for i in range(3):
fd(10 * k)
rt(120)
end_fill()
sc = getcanvas()
count = 0
for x in range(1, 10):
for y in range(1, 10):
items = sc.find_overlapping(x * k, -y * k, x * k, -y * k)
if items and sc.itemcget(items[-1], "fill") == "red":
count += 1
print(count)
done()
fillcolor("red")- каким цветом заливать.begin_fill()…end_fill()- всё, что черепаха нарисует между этими командами, будет залито. Заливается один замкнутый контур, поэтому повторов должно быть ровно столько, сколько нужно для одного обхода: для треугольника 3, а не 7, как бывает в условии.sc = getcanvas()- холст, на котором нарисована фигура.sc.find_overlapping(x1, y1, x2, y2)- все нарисованные объекты в прямоугольнике холста; у нас это одна точка. У холста ось y направлена вниз, поэтому точке (x; y) соответствует пиксель (x · k; −y · k).sc.itemcget(items[-1], "fill")- цвет заливки верхнего объекта в этой точке. Если он красный - точка внутри фигуры.range(1, 10)- перебираем точки строго внутри треугольника со стороной 10.
Автоподсчёт не всегда точен для точек прямо на линии, поэтому результат полезно сверить с рисунком.
