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

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

let result = "";

for (let i = 0; i < 10; i++) {
  for (let j = 0; j < 10; j++) {
    result += "*";
  }

  console.log(result);
  result = "";
}

Запознахме се и с метода str.repeat(count), моето дава възможност даден стринг да се печата определен от нас брой пъти:

'abc'.repeat(2); // 'abcabc'

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

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

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

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

Входни данни

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

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

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

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

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

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

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

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

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

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

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

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

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

За да отпечатаме на конзолата покрива, ще използваме метода repeat(n), която съединява даден стринг n на брой пъти.

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

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

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

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

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

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

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

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

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

Входни данни

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

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

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

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

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

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

Аналогично на предходната задача виждаме от условието, че входните данни ще се състоят само от едно цяло число в интервала [3 … 1000]. Създаваме функция, която приема като аргумент масив от един елемент. Тъй като той е от тип текст (String), а ние трябва да работим с числа, използваме конструктора Number() като функция, с която да го конвертираме:

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

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

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

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

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

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

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

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

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

Входни данни

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

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

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

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

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

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

Както при предните задачи, създаваме функция, която приема масив от един елемент и с Number() го преобразуваме от тип текст (String) към число:

Можем да разделим фигурата на 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.org/Contests/Practice/Index/936#2.

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

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

Входни данни

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

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

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

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

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

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

Както при предните задачи, създаваме функция, която приема масив от един елемент и с Number() го преобразуваме от текст към число:

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

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

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

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

Средата на фигурата е съставена от начало #, среда . и край #. Броят на # е равен на outerDots + 1:

За да начертаем долната част на стрелката, трябва да зададем нови стойности на двете променливи outerDots и innerDots:

При всяка итерация outerDots се увеличава с 1, а innerDots намалява с 2. Забелязваме, че тъй като на предпоследния ред стойността на innerDots ще е 1 при последвала итерация на цикъла тя ще стане отрицателно число. Ако използваме метода str.repeat(count) с отрицателно число, програмата ни ще даде грешка. Един вариант да избегнем това е да отпечатаме последния ред на фигурата отделно.

Височината на долната част на стрелката е n - 1, следователно цикълът, който ще отпечата всички редове без последния, трябва да се завърти n - 2 пъти:

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

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

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

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

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

Входни данни

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

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

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

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

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

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

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

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

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

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

От примерите можем да разберем, каква е структурата на дръжката:

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

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

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

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

results matching ""

    No results matching ""