Глава 6.2. Вложени цикли – изпитни задачи

В предходната глава разгледахме вложените цикли и как да ги използване за рисуване на различни фигури на конзолата. Научихме се как да отпечатваме фигури с различни размери, измисляйки подходяща логика на конструиране с използване на единични и вложени for цикли в комбинация с различни изчисления и програмна логика:

for r in range(5):
    print("*", end="")
    for c in range(5):
        print(" *", end="")
    print("")

Запознахме се и с оператора *, който дава възможност даден стринг да се печата определен от нас брой пъти:

print_me = ('abc' * 2)

Изпитни задачи

Сега нека решим заедно няколко изпитни задачи, за да затвърдим наученото и да развием още алгоритмичното си мислене.

Задача: чертане на крепост

Да се напише програма, която приема цяло число n и чертае крепост с ширина 2 * n колони и височина n реда като в примерите по-долу. Лявата и дясната колона във вътрешността си са широки n / 2.

Входни данни

Входът на програмата се състои от един елемент (аргумент) - цяло число n в интервала [3 … 1000].

Изходни данни

Да се отпечатат на конзолата n текстови реда, изобразяващи крепостта, точно както в примерите.

Примерен вход и изход

Вход Изход Вход Изход
3 /^\/^\
|    |
\_/\_/
4 /^^\/^^\
|      |
|      |
\__/\__/
Вход Изход Вход Изход
5 /^^\__/^^\
|        |
|        |
|   __   |
\__/  \__/
8 /^^^^\____/^^^^\
|              |
|              |
|              |
|              |
|              |
|     ____     |
\____/    \____/

Насоки и подсказки

От условието на задачата виждаме, че входните данни ще се състоят само от един ред, който ще съдържа в себе си едно цяло число в интервала [3 … 1000]. По тази причина ще конвертираме прочетената стрингова стойност към тип int:

След като вече сме декларирали и инициализирали входните данни, ще разделим крепостта на три части:

  • покрив
  • тяло
  • основа

От примерите можем да разберем, че покривът е съставен от две кули и междинна част. Всяка кула се състои от начало /, среда ^ и край \.

По условие лявата и дясната колона във вътрешността си са широки n / 2, следователно можем да отделим тази стойност в отделна променлива, като внимаваме, че ако като входни данни имаме нечетно число, при деление на две резултатът ще е реално число с цяла и дробна част. Тъй като в този случай ни трябва само цялата част (в условието на задачата виждаме, че при вход 3 броят на ^ във вътрешността на колоната е 1, а при вход 5 е 3), можем да я отделим с метода math.trunc(…) и да запазим само нейната стойност в новата ни променлива:

Добра практика е винаги, когато видим, че имаме израз, чиято стойност ще използваме повече от един път, да запазваме стойността му в променлива. Така от една страна, кодът ни ще бъде по-лесно четим, от друга страна, ще бъде по-лесно да поправим евентуални грешки в него, тъй като няма да се налага да търсим поотделно всяка употреба на израза.

Декларираме и втора променлива, в която ще пазим стойността на частта между двете кули. Знаем, че по условие общата ширина на крепостта е n * 2. Освен това имаме и две кули с по една наклонена черта за начало и край (общо 4 знака) и ширина colSize. Следователно, за да получим броя на знаците в междинната част, трябва да извадим размера на кулите от ширината на цялата крепост: 2 * n - 2 * colSize - 4:

За да отпечатаме на конзолата покрива, ще използваме метода format(…) в комбинация с оператора *, който съединява даден символ в низ n на брой пъти:

\ е специален символ в езика Python и използвайки само него в метода print(…), конзолата няма да го разпечата, затова с \\ показваме на конзолата, че искаме да отпечатаме точно този символ, без да се интерпретира като специален (екранираме го, на английски се нарича “character escaping”).

Тялото на крепостта се състои от начало |, среда (празно място) и край |. Средата от празно място е с големина 2 * n - 2. Броят на редовете за стени, можем да определим от дадените ни примери - n - 3:

За да нарисуваме предпоследния ред, който е част от основата, трябва да отпечатаме начало |, среда (празно място)_(празно място) и край |. За да направим това, можем да използваме отново вече декларираните от нас променливи col_size и mid_size, защото от примерите виждаме, че са равни на броя _ в покрива:

Добавяме към стойността на празните места + 1, защото в примерите имаме едно празно място повече.

Структурата на основата на крепостта е еднаква с тази на покрива. Съставена е от две кули и междинна част. Всяка една кула има начало \, среда _ и край /:

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/1056#0.

Задача: пеперуда

Да се напише програма, която приема цяло число n и чертае пеперуда с ширина 2 * n - 1 колони и височина 2 * (n - 2) + 1 реда като в примерите по-долу. Лявата и дясната ѝ част са широки n - 1.

Входни данни

Входът се състои от един елемент (аргумент) - цяло число n [3 … 1000].

Изходни данни

Да се отпечатат на конзолата 2 * (n - 2) + 1 текстови реда, изобразяващи пеперудата, точно както в примерите.

Примерен вход и изход

Вход Изход Вход Изход
3 *\ /*
  @  
*/ \*
5 ***\ /***
---\ /---
***\ /***
    @    
***/ \***
---/ \---
***/ \***
Вход Изход
7 *****\ /*****
-----\ /-----
*****\ /*****
-----\ /-----
*****\ /*****
      @      
*****/ \*****
-----/ \-----
*****/ \*****
-----/ \-----
*****/ \*****

Насоки и подсказки

Аналогично на предходната задача виждаме от условието, че входните данни ще се състоят само от едно цяло число в интервала [3 … 1000]. По тази причина ще конвертираме прочетената стойност към тип int:

Можем да разделим фигурата на 3 части - горно крило, тяло и долно крило. За да начертаем горното крило на пеперудата, трябва да го разделим на части - начало *, среда \ / и край *. След разглеждане на примерите можем да кажем, че горното крило на пеперудата е с големина n - 2:

За да нарисуваме горното крило правим цикъл, който да се повтаря half_row_size пъти:

От примерите можем също така да забележим, че на четен ред имаме начало *, среда \ / и край *, а на нечетен - начало -, среда \ / и край -. Следователно при всяка итерация на цикъла трябва да направим if-else проверка дали редът, който печатаме, е четен или нечетен. От примерите, дадени в условието, виждаме, че броят на звездичките и тиретата на всеки ред също е равен на n - 2, т. е. за тяхното отпечатване отново можем да използваме променливата half_row_size:

За да направим тялото на пеперудата е необходимо да отпечатаме на конзолата точно един ред. Структурата на тялото е с начало (празно място), среда @ и край (празно място). От примерите виждаме, че броят на празните места е n - 1:

Остава да отпечатаме на конзолата и долното крило, което е аналогично на горното крило: единствено трябва да разменим местата на наклонените черти.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/1056#1.

Задача: знак "Стоп"

Да се напише програма, която приема цяло число n и чертае предупредителен знак STOP с размери като в примерите по-долу.

Входни данни

Входът е състоящ се от един елемент (аргумент)- цяло число n в интервала [3 … 1000].

Изходни данни

Да се отпечатат на конзолата текстови редове, изобразяващи предупредителния знак STOP, точно както в примерите.

Примерен вход и изход

Вход Изход Вход Изход
3 ...._______....
...//_____\\...
..//_______\\..
.//_________\\.
//___STOP!___\\
\\___________//
.\\_________//.
..\\_______//..
6 ......._____________.......
......//___________\\......
.....//_____________\\.....
....//_______________\\....
...//_________________\\...
..//___________________\\..
.//_____________________\\.
//_________STOP!_________\\
\\_______________________//
.\\_____________________//.
..\\___________________//..
...\\_________________//...
....\\_______________//....
.....\\\___________//.....
Вход Изход
7 ........_______________........
.......//_____________\\.......
......//_______________\\......
.....//_________________\\.....
....//___________________\\....
...//_____________________\\...
..//_______________________\\..
.//_________________________\\.
//___________STOP!___________\\
\\___________________________//
.\\_________________________//.
..\\_______________________//..
...\\_____________________//...
....\\___________________//....
.....\\_________________//.....
......\\_______________//......

Насоки и подсказки

Както и при предходните задачи, входните данни ще бъдат прочетени само от един ред, който ще съдържа в себе си едно цяло число в интервала [3 … 1000]:

Можем да разделим фигурата на 3 части - горна, средна и долна. Горната част се състои от две подчасти - начален ред и редове, в които знака се разширява. Началния ред е съставен от начало ., среда _ и край .. След разглеждане на примерите можем да кажем, че началото е с големина n + 1 и е добре да отделим тази стойност в отделна променлива. Трябва да създадем и втора променлива, в която ще пазим стойността на средата на началния ред с големина 2 * n + 1:

След като вече сме декларирали и инициализирали двете променливи, можем да отпечатаме на конзолата началния ред:

За да начертаем редовете, в които знака се "разширява", трябва да създадем цикъл, който да се завърти n брой пъти. Структурата на един ред се състои от начало ., // + среда _ + \\ и край .. За да можем да използваме отново създадените променливи, трябва да намалим dots с 1 и underscores с 2, защото ние вече сме отпечатали първия ред, а точките и долните черти в горната част от фигурата на всеки ред намаляват:

На всяка следваща итерация началото и краят намаляват с 1, а средата се увеличава с 2:

Средната част от фигурата има начало // + _, среда STOP! и край _ + \\. Броят на долните черти _ е (underscores - 5) / 2:

Долната част на фигурата, в която знака се смалява, можем да направим като отново създадем цикъл, който да се завърти n брой пъти. Структурата на един ред е начало . + \\, среда _ и край // + .. Броят на точките при първата итерация на цикъла трябва да е 0 и на всяка следваща да се увеличава с едно. Следователно можем да кажем, че броят на точките в долната част от фигурата е равен на i. За да работи нашата програма правилно, трябва на всяка итерация от цикъла да намаляваме броя на _ с 2:

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/1056#2.

Задача: стрелка

Да се напише програма, която приема цяло нечетно число n и чертае вертикална стрелка с размери като в примерите по-долу.

Входни данни

Входът се състои от цяло нечетно число n (аргумент) в интервала [3 … 79].

Изходни данни

Да се отпечата на конзолата вертикална стрелка, при която "#" (диез) очертава стрелката, а "." - останалото.

Примерен вход и изход

Вход Изход Вход Изход
3 .###.
.#.#.
##.##
.#.#.
..#..
5 ..#####..
..#...#..
..#...#..
..#...#..
###...###
.#.....#.
..#...#..
...#.#...
....#....
Вход Изход
9 ....#########....
....#.......#....
....#.......#....
....#.......#....
....#.......#....
....#.......#....
....#.......#....
....#.......#....
#####.......#####
.#.............#.
..#...........#..
...#.........#...
....#.......#....
.....#.....#.....
......#...#......
.......#.#.......
........#........

Насоки и подсказки

От условието на задачата виждаме, че входните данни ще бъдат прочетени само от един ред, който ще съдържа в себе си едно цяло число в интервала [3 … 1000]. По тази причина отново ще конвертираме прочетената стойност към тип int:

Можем да разделим фигурата на 3 части - горна, средна и долна. Горната част се състои от две подчасти - начален ред и тяло на стрелката. От примерите виждаме, че броят на външните точки в началния ред и в тялото на стрелката са (n - 1) / 2. Тази стойност можем да запишем в променлива outer_dots:

Броят на вътрешните точки в тялото на стрелката е (n - 2). Трябва да създадем променлива с име inner_dots, която ще пази тази стойност:

От примерите можем да видим структурата на началния ред. Трябва да използваме декларираните и инициализирани от нас променливи outer_dots и n, за да отпечатаме началния ред:

За да нарисуваме на конзолата тялото на стрелката, трябва да създадем цикъл, който да се повтори n - 2 пъти:

Средата на фигурата е съставена от начало #, среда . и край #. Броят на # е равен на outer_dots и за това можем да използваме отново същата променлива:

За да начертаем долната част на стрелката, трябва да зададем нови стойности на двете променливи outer_dots и inner_dots.

При всяка итерация outer_dots се увеличава с 1, а inner_dots намалява с 2. Забелязваме, че тъй като на предпоследния ред стойността на inner_dots ще е 1 и при последвала итерация на цикъла тя ще стане отрицателно число. Тъй като оператора * не може да съедини символ 0 или отрицателен брой пъти в низ, няма да се изведе нищо на конзолата. Един вариант да избегнем това е да отпечатаме последния ред на фигурата отделно. Височината на долната част на стрелката е n - 1, следователно цикълът, който ще отпечата всички редове без последния, трябва да се завърти n - 2 пъти:

Последният ред от нашата фигура е съставен от начало ., среда # и край .. Броят на . е равен на outer_dots:

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/1056#3.

Задача: брадва

Да се напише програма, която приема цяло число n и чертае брадва с размери, показани по-долу. Ширината на брадвата е 5 * n колони.

Входни данни

Входът се състои от един елемент (аргумент) - цяло число n в интервала [2..42].

Изходни данни

Да се отпечата на конзолата брадва, точно както е в примерите.

Примерен вход и изход

Вход Изход Вход Изход
2 ------**--
------*-*-
*******-*-
------***-
5 ---------------**--------
---------------*-*-------
---------------*--*------
---------------*---*-----
---------------*----*----
****************----*----
****************----*----
---------------*----*----
--------------********---
Вход Изход
8 ------------------------**--------------
------------------------*-*-------------
------------------------*--*------------
------------------------*---*-----------
------------------------*----*----------
------------------------*-----*---------
------------------------*------*--------
------------------------*-------*-------
*************************-------*-------
*************************-------*-------
*************************-------*-------
*************************-------*-------
------------------------*-------*-------
-----------------------*---------*------
----------------------*-----------*-----
---------------------***************----

Насоки и подсказки

От условието на задачата виждаме, че входните данни ще бъдат прочетени само от един ред, който ще съдържа в себе си едно цяло число в интервала [2 … 42]. По тази причина ще използваме тип int. След което, за решението на задачата е нужно първо да изчислим големината на тиретата от ляво, средните тирета, тиретата от дясно и цялата дължина на фигурата:

След като сме декларирали и инициализирали променливите, можем да започнем да изчертаваме фигурата като започнем с горната част. От примерите можем да разберем каква е структурата на първия ред и да създадем цикъл, който се повтаря n на брой пъти. При всяка итерация от цикъла средните тирета се увеличават с 1, а тиретата отдясно се намаляват с 1:

Сега следва да нарисуваме дръжката на брадвата. За да можем да използваме отново създадените променливи при чертането на дръжката на брадвата, трябва да намалим средните тирета с 1, а тези отдясно и отляво да увеличим с 1.

Дръжката на брадвата можем да нарисуваме, като завъртим цикъл, който се повтаря n / 2 пъти. Можем да отделим тази стойност в отделна променлива, като внимаваме, че ако като входни данни имаме нечетно число, при деление на 2 резултатът ще е реално число с цяла и дробна част. Тъй като в този случай ни трябва само цялата част (от условието на задачата виждаме, че при вход 5 височината на дръжката на брадвата е 2), можем да използваме метода math.trunc(…), с който да запазим само нейната стойност в новата ни променлива axe_height. От примерите можем да разберем, каква е структурата на дръжката:

Долната част на фигурата, трябва да разделим на две подчасти - глава на брадвата и последния ред от фигурата. Главата на брадвата ще отпечатаме на конзолата, като направим цикъл, който да се повтаря axe_height - 1 пъти. На всяка итерация тиретата отляво и тиретата отдясно намаляват с 1, а средните тирета се увеличават с 2:

За последния ред от фигурата, можем отново да използваме трите, вече декларирани и инициализирани променливи left_dashes, middle_dashes, right_dashes.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/1056#4.

results matching ""

    No results matching ""