В настоящата глава ще разгледаме един практически изпит по основи на програмирането, проведен в СофтУни на 18 декември 2016 г. Задачите дават добра представа какво можем да очакваме на изпита за прием. Изпитът покрива изучавания учебен материал от настоящата книга и от курса "Programming Basics" в СофтУни.
Напишете програма, която да пресмята колко километра изминава кола, за която знаем първоначалната скорост (км/ч), времето в минути, след което увеличава скоростта с 10%, второ време, след което намалява скоростта с 5%, и времето до края на пътуването. За да намерите разстоянието трябва да превърнете минутите в часове ( 70 мин = 1.1666 часа).
От конзолата се четат 4 реда:
- Първоначалната скорост в км/ч – цяло число в интервала [1...300].
- Първото време в минути – цяло число в интервала [1...1000].
- Второто време в минути – цяло число в интервала [1...1000].
- Третото време в минути – цяло число в интервала [1...1000].
Да се отпечата на конзолата едно число: изминатите километри, форматирани до втория символ след десетичния знак.
Вход | Изход | Обяснения |
---|---|---|
90 60 70 80 |
330.90 | Разстояние с първоначална скорост - 90 км/ч * 1 час (60 мин) = 90 км След увеличението - 90 + 10% = 99.00 км/ч * 1.166 часа (70 мин) = 115.50 км След намаляването - 99 - 5% = 94.05 км/ч * 1.33 часа (80 мин) = 125.40 км Общо изминати - 330.9 км |
Вход | Изход | Обяснения |
---|---|---|
140 112 75 190 |
917.12 | Разстояние с първоначална скорост - 140 км/ч * 1.86 часa (112 мин) = 261.33 км След увеличението - 140 + 10% = 154.00 км/ч * 1.25 часа (75 мин) = 192.5 км След намаляването - 154.00 - 5% = 146.29 км/ч * 3.16 часа (190 мин) = 463.28 км Общо изминати - 917.1166 км |
Вероятно е подобно условие да изглежда на пръв поглед объркващо и непълно, което придава допълнителна сложност на една лесна задача. Нека разделим заданието на няколко подзадачи и да се опитаме да решим всяка една от тях, което несъмнено ще ни отведе и до крайния резултат:
- Нека първата подзадача бъде да прочетем входните данни, които потребителя въвежда, и да ги запазим в подходящи променливи.
- Изпълнение на основната програмна логика, което в нашия случай се свежда до прости пресмятания на данните, които вече имаме.
- Пресмятане и оформяне на крайния резултат.
Съществената част от програмната логика се изразява в това да пресметнем какво ще бъде изминатото разстояние след всички промени в скоростта. Тъй като по време на изпълнението на програмата, част от данните, с които разполагаме, се променят, то бихме могли да разделим програмния код на няколко логически обособени части:
- Пресмятане на изминатото разстояние с първоначална скорост.
- Промяна на скоростта и пресмятане на изминатото разстояние.
- Последна промяна на скоростта и пресмятане.
- Сумиране.
За прочитането на данните от конзолата използваме следната функция:
По условие входните данни се въвеждат на четири отделни реда, по тази причина следва да изпълним предходния код общо четири пъти.
За извършване на пресмятанията избираме да използваме тип decimal
.
Типът данни за реални числа с десетична точност в C# е 128-битовият тип decimal
. Той има точност от 28 до 29 десетични символа. Минималната му стойност е -7.9×1028, а максималната е +7.9×1028. Стойността му по подразбиране е 0.0м или 0.0М. Символът 'm' накрая указва изрично, че числото е от тип decimal
(по подразбиране всички реални числа са от тип double
). Най-близките до 0 числа, които могат да бъдат записани в decimal
, са ±1.0 × 10-28. Видно е, че decimal
не може да съхранява много големи положителни и отрицателни числа (например със стотици цифри), нито стойности много близки до 0. За сметка на това този тип почти не прави грешки при финансови пресмятания, защото представя числата като сума от степени на числото 10, при което загубите от закръгляния са много по-малки, отколкото когато се използва двоично представяне. Реалните числа от тип decimal
са изключително удобни за пресмятания с пари – изчисляване на приходи, задължения, данъци, лихви и т.н.
Повече за различните типове данни в езика C# може да прочетете тук:
По този начин успяхме да се справим успешно с първата подзадача. Следващата стъпка е да преобразуваме входните данни в подходящи типове, за да можем да извършим необходимите пресмятания. Избираме да използваме тип Int32
или int
, тъй като в условието на задачата e упоменато, че входните данни ще бъдат в определен интервал, за който този тип данни е напълно достатъчен. Преобразуването извършваме по следния начин:
Първоначално запазваме една променлива, която ще използваме многократно. Този подход на централизация ни дава гъвкавост и възможност да променяме цялостния резултат на програмата с минимални усилия. В случай, че се наложи да променим стойността, трябва да го направим само на едно място в кода, което ни спестява време и усилия.
Изминалото време (в часове) пресмятаме като разделим времето на 60 (минутите в един час). Изминатото разстояние намираме като умножим началната скорост с изминалото време (в часове). След това променяме скоростта, като я увеличаваме с 10% по условие. Пресмятанет на процентите, както и следващите изминати разстояния, извършваме по следния начин:
- Интервалът от време (в часове) намираме като разделим зададения интервал в минути на минутите, които се съдържат в един час (60).
- Изминатото разстояние намираме като умножим интервала (в часове) по скоростта, която получихме след увеличението.
- Следващата стъпка е да намалим скоростта с 5%, както е зададено по условие.
- Намираме оставащото разстояние по описания начин в първите две точки.
До този момент успяхме да изпълним две от най-важните подзадачи, а именно приемането на данните и тяхната обработка. Остава ни само да пресметнем крайния резултат. Тъй като по условие се изисква той да бъде форматиран до 2 символа след десетичния знак, можем да го направим по следния начин:
В случай че сте работили правилно и изпълните програмата с входните данни от условието на задачата, ще се уверите, че тя работи коректно.
Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/517#0
Хараламби има събрани пари, с които иска да смени плочките на пода в банята. Като подът е правоъгълник, а плочките са триъгълни. Напишете програма, която да пресмята дали събраните пари ще му стигнат. От конзолата се четат широчината и дължината на пода, както и едната страна на триъгълника с височината към нея. Трябва да пресметнете колко плочки са нужни, за да се покрие пода. Броят на плочките трябва да се закръгли към по-високо цяло число и да се прибавят още 5 броя за фира. От конзолата се четат още – цената на плочка и сумата за работата на майстор.
От конзолата се четат 7 реда:
- Събраните пари.
- Широчината на пода.
- Дължината на пода.
- Страната на триъгълника.
- Височината на триъгълника.
- Цената на една плочка.
- Сумата за майстора.
Всички числа са реални числа в интервала [0.00 ... 5000.00].
На конзолата трябва да се отпечата на един ред:
- Ако парите са достатъчно:
- “{Оставащите пари} lv left.”
- Ако парите НЕ СА достатъчно:
- “You'll need {Недостигащите пари} lv more.”
Резултатът трябва да е форматиран до втория символ след десетичния знак.
Вход | Изход | Обяснения |
---|---|---|
500 3 2.5 0.5 0.7 7.80 100 |
25.60 lv left. | Площ на пода → 3 * 2.5 = 7.5 Площта на плочка → 0.5 * 0.7 / 2 = 0.175 Необходими плочки → 7.5 / 0.175 = 42.857... = 43 + 5 фира = 48 Обща сума → 48 * 7.8 + 100 (майстор) = 474.4 474.4 < 500 → остават 25.60 лева |
Вход | Изход | Обяснения |
---|---|---|
1000 5.55 8.95 0.90 0.85 13.99 321 |
You'll need 1209.65 lv more. | Площ на пода → 5.55 * 8.95 = 49.67249 Площта на плочка → 0.9 * 0.85 / 2 = 0.3825 Необходими плочки → 49.67249 / 0.3825 = 129.86... = 130 + 5 фира = 135 Обща сума → 135 * 13.99 + 321 (майстор) = 2209.65 2209.65 > 1000 → остават 1209.65 лева |
Следващата задача изисква от нашата програма да приема повече входни данни и извърши по-голям брой изчисления, въпреки че решението е идентично. Приемането на данните от потребителя извършваме по добре познатия ни начин. Обърнете внимание, че в раздел Вход в условието е упоменато, че всички входни данни ще бъдат реални числа и поради тази причина бихме използвали тип decimal
.
След като вече разполагаме с всичко необходимо, за да изпълним програмната логика, можем да пристъпим към следващата част. Как бихме могли да изчислим какъв е необходимият брой плочки, които ще бъдат достатъчни за покритието на целия под? Условието, че плочките имат триъгълна форма, би могло да доведе до объркване, но на практика задачата се свежда до съвсем прости изчисления. Бихме могли да пресметнем каква е общата площ на пода по формулата за намиране на площ на правоъгълник, както и каква е площта на една плочка по съответната формула за триъгълник.
За да пресметнем какъв брой плочки са необходими, разделяме площта на пода на площта на една плочка (като не забравяме да прибавим 5 допълнителни броя плочки, както е по условие).
До крайния резултат можем да стигнем, като пресметнем общата сума, която е необходима, за да бъде покрит целия под, като съберем цената на плочките с цената за майстора, която имаме от входните данни. Можем да се досетим, че общият разход за плочките можем да получим, като умножим броя плочки по цената за плочка. Дали сумата, с която разполагаме, ще бъде достатъчна, разбираме като сравним събраните до момента пари (от входните данни) и общите разходи.
Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/517#1
Магазин за цветя предлага 3 вида цветя: хризантеми, рози и лалета. Цените зависят от сезона.
Сезон | Хризантеми | Рози | Лалета |
---|---|---|---|
Пролет/Лято Есен/Зима |
2.00 лв./бр. 3.75 лв./бр. |
4.10 лв./бр. 4.50 лв./бр. |
2.50 лв./бр. 4.15 лв./бр. |
В празнични дни цените на всички цветя се увеличават с 15%. Предлагат се следните отстъпки:
- За закупени повече от 7 лалета през прoлетта – 5% от цената на целия букет.
- За закупени 10 или повече рози през зимата – 10% от цената на целия букет.
- За закупени повече от 20 цветя общо през всички сезони – 20% от цената на целия букет.
Отстъпките се правят по така написания ред и могат да се наслагват! Всички отстъпки важат след оскъпяването за празничен ден!
Цената за аранжиране на букета винаги е 2 лв. Напишете програма, която изчислява цената за един букет.
Входът се чете от конзолата и съдържа точно 5 реда:
- На първи ред е броят на закупените хризантеми – цяло число в интервала [0...200].
- На втория ред е броят на закупените рози – цяло число в интервала [0...200].
- На третия ред е броят на закупените лалета – цяло число в интервала [0...200].
- На четвъртия ред е посочен сезонът – [Spring, Summer, Аutumn, Winter].
- На петия ред е посочено дали денят е празник – [Y - да / N - не].
Да се отпечата на конзолата 1 число – цената на цветята, форматирана до втория символ след десетичния знак.
Вход | Изход | Обяснения |
---|---|---|
2 4 8 Spring Y |
46.14 | Цена: 2*2.00 + 4*4.10 + 8*2.50 = 40.40 лв. Празничен ден: 40.40 + 15% = 46.46 лв. 5% намаление за повече от 7 лалета през пролетта: 44.14 Общо цветята са 20 или по-малко: няма намаление 44.14 + 2 за аранжиране = 46.14 лв. |
Вход | Изход | Обяснения |
---|---|---|
3 10 9 Winter N |
69.39 | Цена: 3*3.75 + 10*4.50 + 9*4.15 = 93.60 лв. Не е празничен ден: няма увеличение 10% намаление за 10 или повече рози през зимата: 84.24 Общо цветята са повече от 20: 20% намаление = 67.392 67.392 + 2 за аранжиране = 69.392 лв. |
Вход | Изход |
---|---|
10 10 10 Autumn N |
101.20 |
След като прочитаме внимателно условието разбираме, че отново се налага да извършваме прости пресмятания, но с разликата, че този път ще са необходими и повече логически проверки. Следва да обърнем повече внимание на това в какъв момент се извършват промените по крайната цена, за да можем правилно да изградим логиката на нашата програма. Отново, удебеленият текст ни дава достатъчно насоки как да подходим. Като за начало, отделяме вече дефинираните стойности в променливи, както направихме и в предишните задачи:
Правим същото и за останалите вече дефинирани стойности:
Следващата ни подзадача е да прочетем правилно входните данни от конзолата. Подхождаме по добре познатия ни вече начин, но този път комбинираме две отделни функции – една за прочитане на ред от конзолата и друга за преобразуването му в числен тип данни:
Нека помислим кой е най-подходящият начин да структурираме нашата програмна логика. От условието става ясно, че пътят на програмата се разделя основно на две части: Пролет/Лято и Есен/Зима. Разделението правим с условна конструкция, като преди това заделяме променливи за цените на отделните цветя, както и за крайния резултат.
Остава ни да извършим няколко проверки относно намаленията на различните видове цветя, в зависимост от сезона, и да модифицираме крайния резултат.
Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/517#2
Напишете програма, която да пресмята статистика на оценки от изпит. В началото програмата получава броя на студентите, явили се на изпита и за всеки студент неговата оценка. На края програмата трябва да изпечата процента на студенти с оценка между 2.00 и 2.99, между 3.00 и 3.99, между 4.00 и 4.99, 5.00 или повече. Също така и средният успех на изпита.
От конзолата се четат поредица от числа, всяко на отделен ред:
- На първия ред – броя на студентите явили се на изпит – цяло число в интервала [1...1000].
- За всеки един студент на отделен ред – оценката от изпита – реално число в интервала [2.00...6.00].
Да се отпечатат на конзолата 5 реда, които съдържат следната информация:
- "Top students: {процент студенти с успех 5.00 или повече}%".
- "Between 4.00 and 4.99: {между 4.00 и 4.99 включително}%".
- "Between 3.00 and 3.99: {между 3.00 и 3.99 включително}%".
- "Fail: {по-малко от 3.00}%".
- "Average: {среден успех}".
Вход | Изход | Обяснения |
---|---|---|
10 3.00 2.99 5.68 3.01 4 4 6.00 4.50 2.44 5 |
Top students: 30.00% Between 4.00 and 4.99: 30.00% Between 3.00 and 3.99: 20.00% Fail: 20.00% Average: 4.06 |
5 и повече - трима = 30% от 10 Между 4.00 и 4.99 - трима = 30% от 10 Между 3.00 и 3.99 - двама = 20% от 10 Под 3 - двама = 20% от 10 Средният успех е: 3 + 2.99 + 5.68 + 3.01 + 4 + 4 + 6 + 4.50 + 2.44 + 5 = 40.62 / 10 = 4.062 |
Вход | Изход |
---|---|
6 2 3 4 5 6 2.2 |
Top students: 33.33% Between 4.00 and 4.99: 16.67% Between 3.00 and 3.99: 16.67% Fail: 33.33% Average: 3.70 |
От условието виждаме, че първо ще ни бъде подаден броя на студентите, а едва след това оценките им. По тази причина първо в една променлива от тип int
ще прочетем броя на студентите. За да прочетем и обработим самите оценки, ще използваме for
цикъл. Стойността на променливата int
ще бъде крайната стойност на променливата i
от цикъла. По този начин всички итерации на цикъла ще прочетат всяка една оценка.
Преди да се изпълни кода от for
цикъла заделяме променливи, в които ще пазим броя на студентите за всяка група: слаби резултати (до 2.99), резултати от 3 до 3.99, от 4 до 4.99 и оценки над 5. Ще ни е необходима и още една променлива, в която да пазим сумата на всички оценки, с помощта на която ще изчислим средната оценка на всички студенти.
Завъртаме цикъла и в него декларираме още една променлива, в която ще запазваме текущата въведена оценка. Променливата ще е от тип double
и на всяка итерация проверяваме каква е стойността ѝ. Според тази стойност, увеличаваме броя на студентите в съответната група с 1, като не забравяме да увеличим и общата сума на оценките, която също следим.
Какъв процент заема дадена група студенти от общия брой, можем да пресметнем като умножим броя на студентите от съответната група по 100 и след това разделим на общия брой студенти.
Обърнете внимание с какъв числен тип данни работите при извършване на тези пресмятания. |
Крайният резултат оформяме по добре познатия ни начин до втория символ след десетичния знак.
Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/517#3
Да се напише програма, която прочита от конзолата цяло число N и чертае коледна шапка с ширина 4 * n + 1 колони и височина 2 * n + 5 реда като в примерите по-долу.
Входът се чете от конзолата – едно цяло число N в интервала [3…100].
Да се отпечата на конзолата коледна шапка, точно както в примерите.
Вход | Изход |
---|---|
4 | ......./|\....... .......\|/....... .......***....... ......*-*-*...... .....*--*--*..... ....*---*---*.... ...*----*----*... ..*-----*-----*.. .*------*------*. *-------*-------* ***************** *.*.*.*.*.*.*.*.* ***************** |
Вход | Изход |
---|---|
7 | ............./|\............. .............\|/............. .............***............. ............*-*-*............ ...........*--*--*........... ..........*---*---*.......... .........*----*----*......... ........*-----*-----*........ .......*------*------*....... ......*-------*-------*...... .....*--------*--------*..... ....*---------*---------*.... ...*----------*----------*... ..*-----------*-----------*.. .*------------*------------*. *-------------*-------------* ***************************** *.*.*.*.*.*.*.*.*.*.*.*.*.*.* ***************************** |
При задачите за чертане с конзолата, най-често потребителят въвежда едно цяло число, което е свързано с общата големина на фигурката, която трябва да начертаем. Тъй като в условието е упоменато как се изчисляват общата дължина и широчина на фигурката, можем да ги използваме за отправни точки. От примерите ясно се вижда, че без значение какви са входните данни, винаги имаме първи два реда, които са с почти идентично съдържание.
......./|\.......
.......\|/.......
Забелязваме също така, че последните три реда винаги присъстват, два от които са напълно еднакви.
*****************
*.*.*.*.*.*.*.*.*
*****************
От тези наши наблюдения можем да изведем формулата за височина на променливата част на коледната шапка. Използваме зададената по условие формула за общата височина, като изваждаме големината на непроменливата част. Получаваме (2 * n + 5) – 5
или 2 * n
.
За начертаването на динамичната или променлива част от фигурката ще използваме цикъл. Размерът на цикъла ще бъде от 0 до широчината, която имаме по условие, а именно 4 * n + 1
. Тъй като тази формула ще използваме на няколко места в кода, е добра практика да я изнесем в отделна променлива. Преди изпълнението на цикъла би следвало да заделим променливи за броя на отделните символи, които участват в динамичната част: точки и тирета. Чрез изучаване на примерите можем да изведем формули и за стартовите стойности на тези променливи. Първоначално тиретата са 0, но броя на точките ясно се вижда, че можем да получим като от общата широчина извадим 3 (броя символи, които изграждат върха на коледната шапка) и след това разделим на 2, тъй като броя точки от двете страни на шапката е еднакъв.
.......***.......
......*-*-*......
.....*--*--*.....
....*---*---*....
...*----*----*...
..*-----*-----*..
.*------*------*.
*-------*-------*
Остава да изпълним тялото на цикъла, като след всяко начертаване намалим броя на точки с 1, а тиретата увеличим с 1. Нека не забравяме да начертаем и по една звездичка между тях. Последователността на чертане в тялото на цикъла е следната:
- Символен низ от точки.
- Звезда.
- Символен низ от тирета.
- Звезда.
- Символен низ от тирета.
- Звезда.
- Символен низ от точки.
В случай че сме работили правилно получаваме фигурки, идентични на тези от примерите.
Тествайте решението си тук: https://judge.softuni.bg/Contests/Practice/Index/517#4
Напишете програма, която да принтира на конзолата всички комбинации от 3 букви в зададен интервал, като се пропускат комбинациите, съдържащи зададена от конзолата буква. Накрая трябва да се изпринтира броят на отпечатаните комбинации.
Входът се чете от конзолата и съдържа точно 3 реда:
- Малка буква от английската азбука за начало на интервала – от 'a' до 'z'.
- Малка буква от английската азбука за край на интервала – от първата буква до 'z'.
- Малка буква от английската азбука – от 'a' до 'z' – като комбинациите, съдържащи тази буква се пропускат.
Да се отпечатат на един ред всички комбинации, отговарящи на условието плюс броят им, разделени с интервал.
Вход | Изход | Обяснения |
---|---|---|
a c b |
aaa aac aca acc caa cac cca ccc 8 | Всички възможни комбинации с буквите 'а', 'b' и 'c' са: aaa aab aac aba abb abc aca acb acc baa bab bac bba bbb bbc bca bcb bcc caa cab cac cba cbb cbc cca ccb ccc Комбинациите, съдържащи 'b', не са валидни. Остават 8 валидни комбинации. |
Вход | Изход |
---|---|
f k h |
fff ffg ffi ffj ffk fgf fgg fgi fgj fgk fif fig fii fij fik fjf fjg fji fjj fjk fkf fkg fki fkj fkk gff gfg gfi gfj gfk ggf ggg ggi ggj ggk gif gig gii gij gik gjf gjg gji gjj gjk gkf gkg gki gkj gkk iff ifg ifi ifj ifk igf igg igi igj igk iif iig iii iij iik ijf ijg iji ijj ijk ikf ikg iki ikj ikk jff jfg jfi jfj jfk jgf jgg jgi jgj jgk jif jig jii jij jik jjf jjg jji jjj jjk jkf jkg jki jkj jkk kff kfg kfi kfj kfk kgf kgg kgi kgj kgk kif kig kii kij kik kjf kjg kji kjj kjk kkf kkg kki kkj kkk 125 |
Вход | Изход |
---|---|
a c z |
aaa aab aac aba abb abc aca acb acc baa bab bac bba bbb bbc bca bcb bcc caa cab cac cba cbb cbc cca ccb ccc 27 |
За последната задача имаме по условие входни данни на 3 реда, които са представени от по един символ от ASCII таблицата (http://www.asciitable.com/). Бихме могли да използваме вече дефинирана функция в езика C#, като преобразуваме входните данни в тип данни char
по следния начин:
Нека помислим как бихме могли да стигнем до крайния резултат. В случай че условието на задачата e да се изпринтират всички от началния до крайния символ (с пропускане на определена буква), как бихме постъпили?
Най-лесният и удачен начин е да използваме цикъл, като преминем през всички символи и принтираме тези, които са различни от буквата, която трябва да пропуснем. Едно от предимствата на езика C#, е че имаме възможност да използваме различен тип данни за циклична променлива:
Резултатът от изпълнението на кода е всички букви от а до z включително, принтирани на един ред и разделени с интервал. Това прилича ли на крайния резултат от нашата задача? Трябва да измислим начин, по който да се принтират по 3 символа, както е по условие, вместо по 1. Изпълнението на програмата много прилича на игрална машина. Там най-често печелим, ако успеем да наредим няколко еднакви символа. Да речем, че на машината имаме места за три символа. Когато спрем на даден символ на първото място, на останалите две места продължават да се изреждат символи от всички възможни. В нашия случай всички възможни са буквите от началната до крайната такава, зададена от потребителя, а решението на нашата програма е идентично на начина, по който работи игралната машина.
Използваме цикъл, който минава през всички символи от началната до крайната буква включително. На всяка итерация на първия цикъл пускаме втори със същите параметри (но само ако буквата на първия цикъл е валидна, т.е. не съвпада с тази, която трябва да изключим по условие). На всяка итерация на втория цикъл пускаме още един със същите параметри и същата проверка. По този начин ще имаме три вложени цикъла, като в тялото на последния ще принтираме символите.
Нека не забравяме, че се изисква от нас да принтираме и общия брой валидни комбинации, които сме намерили, както и че те трябва да се принтират на същия ред, разделени с интервал.
Може да тествате Вашето решение тук: https://judge.softuni.bg/Contests/Practice/Index/517#5