Глава 6.2. Вложени цикли – изпитни задачи
В предходната глава разгледахме вложените цикли и как да ги използване за рисуване на различни фигури на конзолата. Научихме се как да отпечатваме фигури с различни размери, измисляйки подходяща логика на конструиране с използване на единични и вложени for
цикли в комбинация с различни изчисления и програмна логика:
for (int r = 1; r <= 5; r++) {
cout << '*';
for (int c = 1; c < 5; c++) {
cout << " *";
}
cout << endl;
}
Може да тествате примера онлайн: https://repl.it/@vncpetrov/nestedLoops.
Запознахме се и с конструктора string
, който дава възможност да се печата даден символ определен от нас брой пъти:
string printMe (5, '*');
Може да тествате примера онлайн: https://repl.it/@vncpetrov/stringCtor.
Изпитни задачи
Сега нека решим заедно няколко изпитни задачи, за да затвърдим наученото и да развием още алгоритмичното си мислене.
Задача: чертане на крепост
Да се напише програма, която прочита от конзолата цяло число n и чертае крепост с ширина 2 * n колони и височина n реда като в примерите по-долу. Лявата и дясната колона във вътрешността си са широки n / 2.
Входни данни
Входът е цяло число n в интервала [3 … 1000].
Изходни данни
Да се отпечатат на конзолата n текстови реда, изобразяващи крепостта, точно както в примерите.
Примерен вход и изход
Вход | Изход | Вход | Изход |
---|---|---|---|
3 | /^\/^\ | | \_/\_/ |
4 | /^^\/^^\ | | | | \__/\__/ |
Вход | Изход | Вход | Изход |
---|---|---|---|
5 | /^^\__/^^\ | | | | | __ | \__/ \__/ |
8 | /^^^^\____/^^^^\ | | | | | | | | | | | ____ | \____/ \____/ |
Насоки и подсказки
От условието на задачата виждаме, че входните данни ще се състоят само от един ред, който ще съдържа в себе си едно цяло число в интервала [3 … 1000]. По тази причина ще използваме променлива от тип int
:
След като вече сме декларирали и инициализирали входните данни, ще разделим крепостта на три части:
- покрив
- тяло
- основа
От примерите можем да разберем, че покривът е съставен от две кули и междинна част. Всяка кула се състои от начало /
, среда ^
и край \
.
\ е специален символ в езика C++ и използвайки го с обекта и потока за изход cout << , конзолата няма да го разпечата, затова с \\ показваме на конзолата, че искаме да отпечатаме точно този символ, без да се интерпретира като специален (екранираме го, на английски се нарича “character escaping”). |
Средата е с размер, равен на n / 2
, следователно можем да отделим тази стойност в отделна променлива. Тя ще пази големината на средата на кулата.
Декларираме и втора променлива, в която ще пазим стойността на частта между двете кули. Знаем, че по условие общата ширина на крепостта е n * 2
. Освен това имаме и две кули с по една наклонена черта за начало и край (общо 4 знака) и ширина colSize
. Следователно, за да получим броя на знаците в междинната част, трябва да извадим размера на кулите от ширината на цялата крепост: 2 * n - 2 * colSize - 4
:
За да отпечатаме на конзолата покрива, ще използваме два for
цикъла, които ще разпечатат на екрана символите ^
и _
съответно colSize
и midSize
на брой пъти:
\ е специален символ в езика C++ и използвайки само него в функцията cout , конзолата няма да го разпечата, затова с \\ показваме на конзолата, че искаме да отпечатаме точно този символ, без да се интерпретира като специален (екранираме го, на английски се нарича "character escaping"). |
Тялото на крепостта се състои от начало |
, среда (празно място)
и край |
. Средата от празно място е с големина 2 * n - 2
. Броят на редовете за стени, можем да определим от дадените ни примери - n - 3
:
За да нарисуваме предпоследния ред, който е част от основата, трябва да отпечатаме начало |
, среда (празно място)_(празно място)
и край |
. За да направим това, можем да използваме отново вече декларираните от нас променливи colSize
и midSize
, защото от примерите виждаме, че са равни на броя _
в покрива.
Добавяме към стойността на празните места + 1
, защото в примерите имаме едно празно място повече.
Структурата на основата на крепостта е еднаква с тази на покрива. Съставена е от две кули и междинна част. Всяка една кула има начало \
, среда _
и край /
:
Тестване в Judge системата
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/1367#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
:
Виждаме също така, че горното крило на пеперудата е с размер n - 2
, затова можем да направим цикъл, който да се повтаря halfRowSize
пъти.
От примерите можем също така да забележим, че на четен ред имаме начало *
, среда \ /
и край *
, а на нечетен - начало -
, среда \ /
и край -
. Следователно при всяка итерация на цикъла трябва да направим if-else
проверка дали редът, който печатаме, е четен или нечетен. От примерите, дадени в условието, виждаме, че броят на звездичките и тиретата на всеки ред също е равен на n - 2
, т. е. за тяхното отпечатване отново можем да използваме променливата halfRowSize
:
За да направим тялото на пеперудата, можем отново да използваме променливата halfRowSize
и да отпечатаме на конзолата точно един ред. Структурата на тялото е с начало (празно място)
, среда @
и край (празно място)
. От примерите виждаме, че броят на празните места е n - 1
:
Остава да отпечатаме на конзолата и долното крило, което е аналогично на горното крило: единствено трябва да разменим местата на наклонените черти:
Тестване в Judge системата
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/1367#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.org/Contests/Practice/Index/1367#2.
Задача: стрелка
Да се напише програма, която прочита от конзолата цяло нечетно число n и чертае вертикална стрелка с размери като в примерите по-долу.
Входни данни
Входът е цяло нечетно число n в интервала [3 … 79].
Изходни данни
Да се отпечата на конзолата вертикална стрелка, при която "#
" (диез) очертава стрелката, а ".
" - останалото.
Примерен вход и изход
Вход | Изход | Вход | Изход |
---|---|---|---|
3 | .###. .#.#. ##.## .#.#. ..#.. |
5 | ..#####.. ..#...#.. ..#...#.. ..#...#.. ###...### .#.....#. ..#...#.. ...#.#... ....#.... |
Вход | Изход |
---|---|
9 | ....#########.... ....#.......#.... ....#.......#.... ....#.......#.... ....#.......#.... ....#.......#.... ....#.......#.... ....#.......#.... #####.......##### .#.............#. ..#...........#.. ...#.........#... ....#.......#.... .....#.....#..... ......#...#...... .......#.#....... ........#........ |
Насоки и подсказки
От условието на задачата виждаме, че входните данни ще бъдат прочетени само от един ред, който ще съдържа в себе си едно цяло число в интервала [3 … 79]. По тази причина ще използваме променлива от тип int
:
Можем да разделим фигурата на 3 части - горна, средна и долна. Горната част се състои от две подчасти - начален ред и тяло на стрелката. От примерите виждаме, че броят на външните точки в началния ред и в тялото на стрелката са (n - 1) / 2
. Тази стойност можем да запишем в променлива outerDots
:
Броят на вътрешните точки в тялото на стрелката е (n - 2)
. Трябва да създадем променлива с име innerDots
, която ще пази тази стойност:
От примерите можем да видим структурата на началния ред. Трябва да използваме декларираните и инициализирани от нас променливи outerDots
и n
, за да отпечатаме началния ред:
За да нарисуваме на конзолата тялото на стрелката, трябва да създадем цикъл, който да се повтори n - 2
пъти:
Средата на фигурата е съставена от начало #
, среда .
и край #
. От примерите виждаме, че броят на #
е равен на outerDots
, увеличен с 1 и за това можем да използваме отново същата променлива:
За да начертаем долната част на стрелката, трябва да зададем нови стойности на двете променливи outerDots
и innerDots
:
Цикъла, който ще направим, трябва да се завърти n - 2
пъти и отделно ще отпечатаме последния ред от фигурата. На всяка итерация outerDots
се увеличава с 1, а innerDots
намалява с 2.
При всяка итерация outerDots
се увеличава с 1, а innerDots
намалява с 2. Забелязваме, че тъй като на предпоследния ред стойността на innerDots
ще е 1 и при последвала итерация на цикъла тя ще стане отрицателно число. Тъй като конструкторът на string
не може да съедини даден символ нула или отрицателен брой пъти в низ, няма да се изведе нищо на конзолата. Един вариант да избегнем това е да отпечатаме последния ред на фигурата отделно. Височината на долната част на стрелката е n - 1
, следователно цикълът, който ще отпечата всички редове без последния, трябва да се завърти n - 2
пъти:
Последният ред от нашата фигура е съставен от начало .
, среда #
и край .
. Броят на .
е равен на outerDots
:
Тестване в Judge системата
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/1367#3.
Задача: брадва
Да се напише програма, която прочита цяло число N и чертае брадва с размери, показани по-долу. Ширината на брадвата е 5 * N колони.
Входни данни
Входът е цяло число N в интервала [2..42].
Изходни данни
Да се отпечата на конзолата брадва, точно както е в примерите.
Примерен вход и изход
Вход | Изход | Вход | Изход |
---|---|---|---|
2 | ------**-- ------*-*- *******-*- ------***- |
5 | ---------------**-------- ---------------*-*------- ---------------*--*------ ---------------*---*----- ---------------*----*---- ****************----*---- ****************----*---- ---------------*----*---- --------------********--- |
Вход | Изход |
---|---|
8 | ------------------------**-------------- ------------------------*-*------------- ------------------------*--*------------ ------------------------*---*----------- ------------------------*----*---------- ------------------------*-----*--------- ------------------------*------*-------- ------------------------*-------*------- *************************-------*------- *************************-------*------- *************************-------*------- *************************-------*------- ------------------------*-------*------- -----------------------*---------*------ ----------------------*-----------*----- ---------------------***************---- |
Насоки и подсказки
От условието на задачата виждаме, че входните данни ще бъдат прочетени само от един ред, който ще съдържа в себе си едно цяло число в интервала [2 … 42]. По тази причина ще използваме тип int
. След което, за решението на задачата е нужно първо да изчислим големината на тиретата от ляво, средните тирета, тиретата от дясно и цялата дължина на фигурата:
След като сме декларирали и инициализирали променливите, можем да започнем да изчертаваме фигурата като започнем с горната част. От примерите можем да разберем каква е структурата на първия ред и да създадем цикъл, който се повтаря n
на брой пъти. При всяка итерация от цикъла средните тирета се увеличават с 1, а тиретата отдясно се намаляват с 1:
Сега следва да нарисуваме дръжката на брадвата. За да можем да използваме отново създадените променливи при чертането на дръжката на брадвата, трябва да намалим средните тирета с 1, а тези отдясно и отляво да увеличим с 1:
Дръжката на брадвата можем да нарисуваме, като завъртим цикъл, който се повтаря n / 2
пъти. От примерите можем да разберем, каква е нейната структура:
Долната част на фигурата, трябва да разделим на две подчасти - глава на брадвата и последния ред от фигурата. Главата на брадвата ще отпечатаме на конзолата, като направим цикъл, който да се повтаря n / 2 - 1
пъти. На всяка итерация тиретата от ляво и тиретата от дясно намаляват с 1, а средните тирета се увеличават с 2:
За последния ред от фигурата, можем отново да използваме трите, вече декларирани и инициализирани променливи leftDashes
, middleDashes
, rightDashes
:
Тестване в Judge системата
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/1367#4.