Как составить японский кроссворд

Как составить японский кроссворд

Японские кроссворды

Японский кроссворд — головоломка, в которой с помощью цифр зашифровано некоторое изображение. Целью головоломки является полное восстановление этого изображения.

Японские кроссворды делятся на два вида — черно-белые и цветные. В черно-белых кроссвордах изображение содержит только два цвета — черный (которым мы и рисуем) и белый (цвет фона). В цветных кроссвордах изображение создается несколькими цветами на белом фоне.


Черно-белый японский кроссворд «Домик»

Цветной японский кроссворд «Дискета»

Поле японского кроссворда расчерчено горизонтальными и вертикальными линиями разной толщины. Самые толстые линии отделяют центральную часть (поле для картинки) от цифр. Более тонкими линиями, поле делится на группы по 5 клеток (как по горизонтали, так и по вертикали) — это сделано исключительно для удобства (удобнее считать ширину/высоту групп клеток). Само изображение в японском кроссворде формируется путем закрашивания отдельных клеток (центральной части) в нужный цвет. Не закрашенная клетка при этом считается белой.

Цифры, указанные слева и сверху кроссворда — описывают группы закрашенных клеток (идущих подряд, без пропусков) по горизонтали и вертикали соответственно. Причем порядок этих цифр описывает порядок расположения этих групп, но где каждая группа начинается и заканчивается — не известно (фактически, определить их положение и является задачей головоломки). Каждая отдельная цифра обозначает отдельную группу заданного размера (т.е. цифра 5 — обозначает группу из пяти закрашенных подряд клеток, 1 — группу из одной единственной закрашенной клетки). В черно-белых кроссвордах мы всегда закрашиваем клетку черным цветом, в цветных — закрашиваем клетку тем цветом, которым помечена цифра. Между группами одного цвета должна быть как минимум одна не закрашенная клетка (просто иначе они считались бы одной группой), между группами разных цветов пустых клеток может и не быть.

Главным требованием к японским кроссвордам является то, что кроссворд должен иметь единственное логическое решение, достижимое без различных "угадываний" (метод проб и ошибок). Но, к сожалению, достаточно часто можно встретить кроссворды, имеющие несколько вариантов решения, либо кроссворды, которые нельзя решить чисто аналитическими методами. Порой встречаются даже кроссворды, имеющие ошибки (точнее опечатки), которые делают кроссворд вовсе нерешаемым. По этой причине мы рекомендуем новичкам не обращать внимание на дешевые газеты/журналы с японскими кроссвордами, и с большой осторожностью относится к японским кроссвордам в газетах, не специализирующихся на данном виде кроссвордов, т.к. в таких изданиях очень часто встречаются ошибки. Также хотим заметить, что на все кроссворды, расположенные на нашем сайте, мы даем гарантию, что в них отсутствуют ошибки и все они имеют ровно одно решение, достижимое без "угадываний".

Как решать японские кроссворды

Решение цветных кроссвордов и черно-белых немного отличаются (т.к. в черно-белых кроссвордах не нужно учитывать цвет клеток, правила немного упрощены) — поэтому пока мы поговорим исключительно о черно-белых кроссвордах.

При решении японских кроссвордов человек рассматривает каждую строку/столбец в отдельности, постоянно переходя к следующим столбцам и строкам. При этом процесс решения в каждой строке/столбце сводится к:

  1. Определение клеток, которые точно будут закрашены (при любом возможном расположении групп) — их мы и закрашиваем.
  2. Определение клеток, в которых наличие закрашенных клеток невозможно — такие клетки зачеркиваются крестиком (иногда вместо крестика используется жирная точка).
  3. Определение цифр, положение которых уже вычислено — обычно эти цифры зачеркиваются.

Таким образом, постепенно на поле появляются пометки, которые на следующем шаге помогают вычислить новые метки, потом еще и еще, до тех пор, пока кроссворд не будет полностью разгадан (стоит отметить, что если хотя бы одна пометка была поставлена ошибочно — это может привести к тупиковому решению).

Пример решения

Итак, давайте попробуем решить простейший черно-белый кроссворд:

Перед нами простейший кроссворд размером 9×9 клеток. Мы будем постепенно разгадывать данный кроссворд, объясняя каждый шаг. Чтобы Вы не запутались, новые пометки мы будем отмечать синим цветом.
Сначала посмотрим, имеются ли в кроссворде строки, которые должны быть полностью закрашены. Оказывается, есть — в нашем случае это цифра 9 в четвертой строке. Т.к. ширина кроссворда как раз и составляет 9 клеток — значит, все клетки в этой строке должны быть закрашены. Заодно зачеркиваем саму цифру 9, чтобы она нас не отвлекала.
По аналогии ищем столбцы, которые должны быть полностью закрашены.
Посмотрим на третью строку. Запомним маленькое правило, которое нам очень поможет — если число рядом со строкой или столбцом всего одно и составляет больше половины длины, то можно закрашивать несколько клеток в середине. В нашем случае это центральные пять клеток. Почему? Как ни размещай в девяти клетках группу из семи клеток, пять центральных всегда окажутся закрашенными (чтобы это вычислить, можно из ширины кроссворда вычесть значение цифры — получим цифру 2, которая означает количество "неизвестных" клеток слева и справа, а остальные центральные пять клеток — закрашиваем).
Теперь мы можем отметить крестиками (или точками) клеточки, которые однозначно не могут быть закрашены. Взглянем на первую строку — она полностью отгадана, т.к. у нас уже есть одна закрашенная клеточка, а больше закрашенных клеток в ней быть и не должно. Значит, все остальные клетки помечаем крестиками. Аналогично в шестой и седьмой строках. Не забываем зачеркивать цифры в разгаданных строках.
В пятой строке у нас есть одна закрашенная клетка, и т.к. в данной строке кроме единичных клеток больше ничего нет, мы можем пометить крестиками клетки слева/справа от разгаданной. Зачеркивать цифры мы не можем, т.к. хоть мы и отгадали одну цифру, мы точно не знаем какую именно. Аналогичная ситуация в восьмой строке. Также в девятой строке мы можем точно сказать, что первые две клетки и две последние точно будут не закрашены. Почему? Просто у нас в данной строке уже разгадана одна клетка, и единственная цифра в данной строке — тройка, должна быть частью этой закрашенной клетки.
Теперь посмотрим на первый столбец — также как и в предыдущем шаге, у нас имеется лишь одна цифра в данном столбце — двойка, и одна разгаданная клетка. Соответственно, первые две, и последние четыре клетки — точно будут не закрашены. Аналогичная ситуация во втором и последних четырех столбцах.
Можно заметить, что в центральных пяти столбцах осталось очень мало пустых клеток, даже более того — их количество точь-в-точь соответствует цифрам, указанных сверху. Значит, все эти клетки можно закрасить.
Перейдя к строкам, мы можем увидеть, что вторая и две последних строки уже решены. А в пятой строке мы можем поставить крестики слева и справа от разгаданных клеток, т.к. кроме единичных клеток в данной строке ничего нет.
Теперь мы можем увидеть, что в пятой строке остались только две свободные клетки, как раз под две оставшиеся единички. (стоит отметить, что пятую строку можно было разгадать еще с самого начала, т.к. в девяти клетках расположить пять единичных клеток одного цвета можно только одним возможным способом)
Перейдя к столбцам, мы видим, что первый и последний столбцы уже разгаданы. Остается лишь закрасить последние клетки во втором и восьмом столбцах, и. Поздравляем! Кроссворд полностью разгадан!
Читайте также:  Как закодировать текст в ворде

Теперь мы Вам очень рекомендуем решить данный кроссворд еще раз, но теперь — самостоятельно: перейти к кроссворду.

Получилось? Отлично! Дальше мы рекомендуем Вам решать кроссворды в данном порядке: перейти к списку кроссвордов. Мы лишь пожелаем Вам успехов и приятной игры!

Японский кроссворд — головоломка, в которой по набору чисел нужно воссоздать исходное черно-белое изображение. Каждой строке и каждому столбцу пикселей соответствует свой набор, каждое число в котором, в свою очередь, соответствует длине блока подряд идущих черных пикселей. Между такими блоками должен быть хотя бы один белый пиксель, но точное их число неизвестно. Журналы, целиком посвященные этим головоломкам, есть в большинстве газетных киосков, так что, думаю, почти все с ними хоть раз да встречались, и потому более подробное описание здесь можно не приводить.

В какой-то момент мне захотелось «научить компьютер» решать японские кроссворды так, как решаю их я сам. Никакой высокой цели, just for fun. Потом уже были добавлены способы, которые сам я применять не могу в силу ограниченных возможностей человеческого мозга, но, справедливости ради, со всеми кроссвордами из журналов программа справляется и без них.

Итак, задача простая: решить кроссворд, а если решений много, то найти их все. Решение написано на Haskell’е, и, хотя код достаточно существенно дополняет словесное описание, даже без знания языка общую суть понять можно. Если хочется пощупать результат вживую, на странице пректа можно скачать исходники (бинарных сборок не выкладывал). Решения экспортируются в Binary PBM, из него же можно извлекать условия.

Несмотря на то, что я пытался писать максимально понятно, это не в полной мере мне удалось. Под катом очень много букв и кода и почти нет картинок.

BitMask

В основе всей программы лежит свой велосипед для битовой маски. Он не слишком быстр, но обладает свойством, которое было для меня важно в процессе отладки: падает при операциях, не имеющих смысла, а именно при любой операции над масками разной длины. Я приведу здесь лишь сигнатуры функций и картинку, поясняющую принцип их работы; реализация весьма примитивна и непосредственно к решению отношения не имеет.

Думаю, что такое графическое описание исчерпывающе для всех функций, кроме, пожалуй, bmLeftIncursion и bmRightIncursion . Зачем они нужны, будет ясно позже, принцип же их работы следующий: bmLeftIncursion находит самый левый заполненный бит и создает маску, в которой заполнены все биты до него, а также столько бит начиная с него, сколько было указано при вызове функции; вторая функция работает аналогично.

Структура

Так как решение кроссворда происходит по линиям, то тип, соответствующий всему полю, представляет собой набор всех горизонтальных и вертикальных линий, хоть это и приводит к дублированию всех клеток кроссворда.

Каждая линия хранит информацию о клетках и блоках (блок соответствует числу в условии).

Информация о клетках хранится в виде двух битовых масок одинаковой длины, представляющих закрашенные и заблокированные клетки.

Блок же, помимо непосредственно числа, содержит маску, которая соответствует той области линии, в которой данный блок может находиться.

В начале решения маски закрашенных и заблокированных клеток пусты, а маска блока, наоборот, полностью заполнена. Это значит, что все клетки пусты, а каждый блок может находиться в любой части линии. Процесс решения сводится к тому, чтобы сузить область каждого блока до размеров, равных его числу, и соответствующим образом заполнить маски.

Завершенность и синхронизация

Все вышеперечисленные типы (кроме BitMask ) являются экземплярами двух классов: Completable и Syncable .

Единственная функция класса Completable показывет «завершенность» объекта. Поле считается завершенным, если завершены все его линии. Линия завершена, если завершены все ее блоки; завершенности маски при этом требовать излишне (она следует из завершенности блоков; почему, опять же, будет ясно чуть позже). Для завершения блока, как уже упоминалось выше, необходимо, чтобы размер его области совпадал с его числом.

Класс Syncable предоставляет функции, которые позволяют свести воедино разные ветки решений. snAverage выделяет из двух веток только общее, а snSync — то, что проявилось хотя бы в одной ветке (можно считать их обобщениями функций bmAnd и bmOr соответственно). snAverageAll и snSyncAll делают абсолютно то же самое, но работают не с двумя объектами, а со списками объектов.

Согласованность

Из описания функций класса Syncable видно, что их результатом является объект, обернутый в монаду Maybe . На самом деле, так проявляется важное понятие согласованности, которое тоже определено для всех вышеперечисленных типов, но в отдельный класс не вынесено из соображений инкапсуляции. Как пример, одна и та же клетка не может быть одновременная закрашенной и заблокированной; если какая-либо операция может привести к такой ситуации, то она помечена монадой Maybe (как правило, имеет тип type TransformFunction a = a -> Maybe a ), и, если она к этой ситуации приводит, то результатом ее будет Nothing , потому что ни один объект в программе не может существовать в несогласованном состоянии. Так как Nothing , в свою очередь, не может являться составной частью других объектов, несогласованным станет все поле, что будет означать отсутствие решений.

Читайте также:  Какой ноутбук нужен для автокада

Согласованность поля обеспечивается синхронизацией горизонтальных и вертикальных линий. Таким образом, если клетка находится в каком-то состоянии (закрашена, заблокирована или пуста) в горизонтальной линии, то она находится точно в том же состоянии в соответствующей вертикальной линии, и наоборот.

О согласованности линии поговорим позже, так как она имеет непосредственное отношение к процессу решения.

Согласованность блока обеспечивается нетривиально: для нее необходимо исключить из области блока те непрерывные части, которые не могут его вместить. Таким образом, если из области блока с числом 3 и исходной областью исключить маску (например, по причине того, что эта клетка оказалась заблокирована), то конечным итогом этой операции будет блок с областью , а вовсе не .

Для маски согласованность очевидна и уже описывалась выше: нельзя одновременно закрасить и заблокировать одну и ту же клетку.

Преобразования

Операции преобразования масок и блоков весьма ограничены, так как в процессе решения клетки можно только закрашивать и блокировать (передумать, взять ластик и стереть уже нельзя), а область блока можно лишь сужать.

Решение

Процесс решения будем рассматривать отдельными частями, пока они, наконец, не сложатся в общую картину.

Согласованность линии

Для начала восстановим пробел, оставленный в разделе про согласованность, и объявим, что линия считается согласованной, если ее маска заполнена в соответствии с ее блоками. За этой фразой скрываются два пункта. Во-первых, должны быть заблокированы те клетки, которые не попадают в область ни к одному блоку (если линия не содержит ни одного блока, то, соответственно, таковыми являются все клетки).

Во-вторых, по каждому блоку при помощи функции blToFillMask можно получить маску, которую неоходимо закрасить. Она является пересечением двух масок, получающихся, если «загнать» блок в самую левую и самую правую части своей области.

(Примечание: здесь мы, наконец, использовали функции bmLeftIncursion и bmRightIncursion . Строго говоря, если бы они применялись только для этой цели, то, скорее всего, выглядели бы немного по-другому, а именно не заполняли бы битовую маску до самого первого заполненного бита исходной маски.)

Таким образом, о чем уже упоминалось ранее, условие согласованности для линии гарантирует, что ее маска всегда будет завершена, если завершены все ее блоки.

Простое преобразование линии

Решение в рамках линии по сути сводится к двум преобразованиям.

Первое преобразование, фактически, является обратным к условию согласованности: оно гарантирует, что блоки будут завершены, если завершена маска. Для этого используется три действия.

    Все заблокированные клетки должны быть исключены из областей всех блоков.

Если блок не может вместить какую-либо непрерывную закрашенную часть маски (то есть если она вылезает за область блока или имеет размер, больший, чем его число), то она должна быть исключена из области блока.

Из области каждого блока должны быть исключены blMinimumLeftMask его левого соседа и blMinimumRightMask правого соседа (вот тут уже они нужны именно в том виде, в котором описаны выше). Если быть точным, то исключаются эти маски, расширенные на одну клетку, так как между блоками должна быть хотя бы одна пустая клетка.

Вместе эти действия образуют следующую функцию (функция slLoop будет описана позже):

Второе преобразование линии

Если взять самый левый из всех блоков, которые в принципе могут содержать некоторую закрашенную часть маски, то его крайнее правое положение будет ограничено этой самой маской, ведь если он сдвинется еще правее, то эту закрашенную область уже некому будет «отдать». Те же соображения верны и для самого правого из таких блоков.

Применяя это рассуждение к каждой непрерывной части маски, получаем второе преобразование линии:

Преобразования поля

Поле каких-то особых собственных преобразований не имеет, единственный вариант для него — взять некоторое готовое преобразование и применить его ко всем своим линиям.

Ветвления

Так как решение японских кроссвордов — NP-полная задача, то без ветвлений обойтись не удастся. Ветвление определим функцией типа type ForkFunction a = a -> [[a]] , где внутренний список включает в себя взаимоисключающие варианты, а внешний — различные способы эти варианты произвести.

Простейший способ — ветвление по клеткам: каждая пустая клетка порождает один элемент внешнего списка, являющийся в свою очередь списком из двух элементов, в одном из которых эта клетка закрашена, а в другом заблокирована.

Для линии также доступен другой способ ветвления: для каждой непрерывной закрашенной части маски (внешний список) можно рассматривать набор блоков, которые могут ее содержать (внутренний список), как варианты, определяющие ветки.

Обобщенные функции

Большую часть преобразований имеет смысл применять итерационно. При этом можно просто применять преобразование до тех пор, пока оно хоть что-то меняет, а можно (в случае, когда лишнее применение может занять значительное время) предварительно проверять объект на завершенность.

Результаты ветвления можно обрабатывать независимо от конкретного типа данных и способа ветвления. Для этого, применив некоторый способ ветвления, а затем применив к каждому получившемуся объекту какую-либо трансформацию, по каждому набору взаимоисключающих веток необходимо взять среднее значение, после чего синхронизировать эти усредненные объекты, полученные различными точками ветвления. Не буду описывать подробно, но для этой операции также доступен оптимизированный вариант, связанный с проверкой на завершенность.

Наконец, если уже ничего не помогает, можно уходить в рекурсию. Только таким способом можно получить все решения, если их несколько.

Fina venko

Все. Имеющихся инструментов достаточно, чтобы получить решатель в несколько простых шагов.

    Скомбинируем два преобразования линии.

Обработаем специфичное для линии ветвление.

Составим из этих двух преобразований преобразование поля.

Обработаем результаты ветвления поля по клеткам.

Объединим предыдущие два преобразования.

И, наконец, добавим рекурсию.

Послесловие

Программа работает довольно быстро на кроссвордах, имеющих единственное решение: примерно из тысячи имеющихся у меня кроссвордов на моем ноутбуке лишь два (включая вынесеный в предисловие) решаются больше минуты, практически все укладываются в 10 секунд, и при этом ни один не потребовал рекурсии.

Теоретически, при некоторой доработке программу можно использовать для автоматической оценки сложности кроссвордов (так как методы решения в целом аналогичны применяемым человеком) и доказательства единственности решения; экспорт в LaTeX имеется, и даже, возможно, скоро появится в SVN’е. Так что при желании можно организовать домашний выпуск журналов 🙂

Читайте также:  Бесплатная активация eset nod32

В течение последних нескольких лет в обиход многих жителей России вошёл новый вид интеллектуального досуга — заполнение японских кроссвордов. Японские кроссворды продаются в недорогих (от 9 рублей) сборниках и сопровождаются инструкциями по заполнению.
К сожалению, инструкции, прилагаемые к печатным сборникам японских короссвордов, не только неполны, но и содержат логические ошибки. В связи с этим мне кажется уместным изложить свою методику заполнения японских кроссвордов, созданную в его ходе благодаря в основном удачной компиляции (подглядыванию через плечо при поездках в метрополитене), но также и систематизации сворованных навыков, и изобретению собственных. Методика выведена на осознанный уровень специально к этому случаю.

Японский кроссворд (реже — японская картинка) в незаполненном виде представляет собой прямоугольное поле, разбитое на клетки. Можно также представить его как таблицу, в пересечениях строк и столбцов которой находятся клетки японского кроссворда. При заполнении всех необходимых клеток японского кроссворда они образуют собой рисунок. Иногда к рисунку прилагается ключевое слово, которое надо угадать (метод угадывания ключевых слов мне неизвестен, оттого здесь не описан). С двух прилежащих друг к другу сторон от поля (обычно сверху и слева) находятся поля чисел.
Для упрощения буду называть размер (длину или ширину) в одну клетку японского кроссворда единицей.
Введу понятие полосы. Полосой называется заполненный (или подлежащий заполнению) непрерывный прямоугольник, одно из измерений которого (длина или ширина) равно единице. Таким образом, этот прямоугольник вписывается в строку (столбец). Величина другого измерения прямоугольника задаётся соответствующим числом в поле чисел.
Описанного выше достаточно для того, чтобы привести пример какого-нибудь простенького, близкого к вырожденному в единицу, японского кроссворда.

🙂 2 1
1
1 1

Можно видеть, что этот кроссворд имеет высоту в 2 единицы (строки), ширину в 4 единицы (столбца) и, соответственно, 8 клеток, которые предстоит заполнить. Разумеется, поля чисел в размер не входят. Кроме того, можно видеть, что в двух средних столбцах не задано ни одной полосы, в верхней строке — полоса длиной в 1 единицу, в нижней строке — две полосы длиной по 1 единице, а в левом и правом столбцах — по полосе длиной в 2 и 1 единицу соответственно.
Неопределённость при заполнении японских кроссвордов обусловлена тем, что длина промежутков между полосами в однй строке (одном столбце) в явном виде не задана. Она должна составлять не менее 1 единицы — вот и всё, что о ней известно. В примере японского кроссворда, приведённом выше, промежуток между двумя полосами в нижней строке составляет 2 единицы, но в полях чисел это не обозначено никак.
Разумеется, японские кроссворды такого размера в сборниках не встречаются. Их обычные длина и ширина варьируют в пределах от 15 до 60 единиц.
Для логического завершения темы приведу здесь японский кроссворд, вырожденный в единицу.

🙂 1
1

Его единственная строка длиной в одну единицу содержит единстенную полосу длиной в одну единицу. Его единственный столбец длиной (высотой) в одну единицу также содержит полосу длиной в одну единицу. Заполните вырожденный японский кроссворд самостоятельно, без инструкций.

Ниже изложены методики заполнения отдельных строк (столбцов) японского кроссворда. При использовании методик учитываются эффекты, возникающие после заполнения пересекающих данную строку столбцов (соответственно, пересекающих данный столбец строк). За счёт этих эффектов можно возвращаться к заполнению одной и той же строки (столбца) достаточно много раз — до тех пор, пока не будет заполнена вся строка (весь столбец). Случается, что некоторые серии строк и столбцов внезапно начинают зарастать после единственной поставленной точки.
Теоретически, существуют незаполнимые однозначным образом японские кроссворды. Например, такую сетку:

🙂 1 1
1
1

можно заполнить двояко:

🙂 1 1
1 ×
1 ×

и

🙂 1 1
1 ×
1 ×

На практике мне несколько раз встречались ситуации, в которых нельзя заполнить японский кроссворд однозначно. Это касалось небольших его участков и не влияло на общую картину. Однако подавляющее большинство японских кроссвордов заполняются однозначно. Перед началом заполнения японского кроссворда следует набраться веры в это и большого терпения.
Ещё о состояниях и фокусах внимания, нужных при заполнении японского кроссворда. Если вам кажется, что у вас складывается в результате заполнения кроссворда цельная картинка, и нужно просто дорисовать её, не поддавайтесь соблазну. Главное — быть до конца последовательным и методичным. Никогда не работайте с целым — работайте с частями (строками и столбцами). Тогда целое придёт само.
Очень важно при работе с японским кроссвордом уметь дробить части на ещё меньшие части и фокусировать внимание на самом малом.
Ещё одна немаловажная деталь. Работу с японским кроссвордом можно отложить и возобновить на любой обработанной строке (столбце). Важно завершить цикл операций со строкой (столбцом), вплоть до обводки и зачёркивания чисел в полях чисел. Между циклами может быть перерыв любой длины — пусть даже годичный. Взяв в руки недозаполненный японский кроссворд, наберитесь веры и терпения, сосредоточьтесь на деталях — и он будет заполняться точно так же, как и девственно чистый. «Вдохновение» и прочие аспекты Высокой Духовности здесь не канают.
Японские кроссворды перестают быть непомерно утомительными в тот момент, когда методы их заполнения отработаны до автоматизма. К сожалению, вскоре они становятся неинтересными. С другой стороны, даже при потере интереса к ним у заполнявшего эти таблички формируется и надолго остаётся определённое «ЯК»-мышление, заключающееся в доверии к самодвижению целого и внимании к деталям. Своего рода «На Аллаха надейся, а верблюда привязывай», причём только в результате многократного привязывания верблюда Аллах и проявит свою милость к караванщику.

Все методы, связанные с заполнением отдельной строки или отдельного столбца, я буду описывать в применении к строкам. Это проще не только для набора текста, но и для восприятия. Для перенесения метода со строки на столбец достаточно мысленно повернуть японский кроссворд на 90 градусов против часовой стрелки.

Ссылка на основную публикацию
Как создать словарь в word
Меня интересует вот такая тема: когда печатаешь текст на компе, например, с ошибкой правописания, появляется подсказка - как правильно надо....
Как сделать кроссворд в ворде 2010
Пользоваться файлами, созданными в MS Office, можно как угодно: открыть документ Ворд онлайн, составить в Экселе квартальный отчёт или в...
Как сделать местный разрез
Основным назначением видов является определение формы внешних поверхностей предмета. Выявление на видах формы внутренних поверхностей при помощи штриховых линий не...
Как создать таблицу на компьютере
Как создать таблицу в Word? Сейчас я просто не представляю, как можно работать без такой нужной и полезной программы, как...
Adblock detector