Олімпіада з програмування 2014

Деякі задачі з розв'язками

Годинник (15 балів)
Старовинний годинник з дванадцятигодинним циферблатом б’є в кінці останньої хвилини кожної години. Бій триває менше однієї хвилини. Кількість ударів відповідає показам годинникової стрілки (1 удар – о першій ночі і о першій дня, по 2 удари – о другій години ночі та о другій дня і так далі, опівночі і опівдні він б’є, відповідно, по 12 разів.
 Дано проміжок часу (відомо, що пройшло строго менше 24 годин). Напишіть програму, що визначає, скільки ударів зробив годинник за цей час.

Вхідні дані: ви вводите з клавіатури у першому рядку початковий момент часу, в другому рядку — кінцевий. Моменти часу задаються двома цілими числами, що розділяються пропуском. Перше число задає години (від 0 до 23, друге – хвилини (від 1 до 59).
Вихідні дані: ви виводите на екран одне число — кількість ударів, які зробив годинник за цей відрізок часу.
24.12
Для розв’язку цієї задачі потрібно використати умовний та циклічний оператор. Для зручності тестування програми я встановив мітку start на початку програми і в кінці програми здійснив перехі на початкову мітку. Таким чином для перевірки не потрібно кожного разу закривати і знову запускати програму.

 З таблиці видно, що в програмі потрібно передбачити три умови:

1)кінцевий момент часу більший за початковий

2)кінцевий момент часу менший за початковий

3)коли кінцевий момент часу дорівнює початковому

Шляхом логічних міркувань визначаємо ще одну умову:

4) кінцевий момент часу більший за початковий і більший за 12

Решту проблем вирішуємо в порядку поступання


 Хід роботи
Program Godynnyk;  // Називаємо програму і оголошуємо змінні
var
g1,h1,g2,h2,i,s:integer; // g1,g2 -початкова і кінцева години;i- циклічна змінна;
//h1,h2 – початкова і кінцева хвилини; s-сума (кількість ударів)
label start;                     // оголошуємо мітку початку програми
begin
start:                               //встановлюємо мітку початку програми
s:=0;
writeln (‘введіть початкову годину і хвилину через пробіл’);
readln (g1,h1);             //вводимо початкові дані
writeln(‘введіть кінцеву годину і хвилину через пробіл’);
readln (g2,h2);
if g2>g1 then      // коли кінцевий момент часу більший за початковий
begin
if g2>12 then      // коли кінцевий час більший за 12
begin                   // спрацовує цей фрагмент програми
for i:=g1+1 to 12 do s:=s+i;      //циклічна змінна змінюється від наступної за початковою годиною
for i:=1 to g2-12 do s:=s+i;     // до 12 години. Враховуємо ще один оберт малої стрілки.
end else begin           // коли кінцевий час менший за 12
for i:=g1+1 to g2 do s:=s+i;  // працює такий рядок
end; end;
if g2<g1 then    // коли кінцевий момент часу менший за початковий
begin
for i:=g1+1 to 12 do s:=s+i; //рахуємо удари годинника від початкової до 12 години
for i:=1 to 12 do s:=s+i;      //рахуємо удари годинника від 1 до 12 год  
for i:=1 to g2 do s:=s+i;     //знову удари годинника від 1 до кінцевої години
end;
if g2=g1 then   // коли кінцевий момент часу дорівнює початковому
begin
s:=0;                 // якщо години співпадають то ударів не буде
end;
writeln (‘Годинник зробив ‘, s, ‘ ударів’);
goto start;        // переходимо на початок програми
end.
 Звичайно це не найоптимальніший алгоритм, але він максимально прозорий і зрозумілий для учнів. Хто уважний той помітив, що в програмі використані тільки години, а хвилини не беруться до уваги.
 Скопіюйте програму до середовища Паскаль(якщо потрібно то допишіть команду Uses), запустіть на виконання.
  Для перевірки роботи програми журі використовували таку тестову таблицю.
Не всі учасники районної олімпіади справилися з першою задачею. Ось програми створені переможцями.

Кричун Максим Рожнівський НВК  10 клас.
program zadacha1;
Var                       //Оголошення змінних
startHour:integer;
endHour:integer;
startMinute:integer;
endMinute:integer;
summa:integer;
i: integer;
begin
summa := 0;                 // зайва команда так як сума на початку програми автоматично=0
Readln(startHour, startMinute);   // читає дані з клавіатури
Readln(endHour, endMinute);
if endHour < startHour then      // перевіряє чи початкова година більша за кінцеву
endHour := endHour + 24;         // якщо так то додаємо 24 години
if startMinute > 0 then          // якщо початкова година >0 
startHour := startHour + 1;      // збільшуємо її на 1
for i:=startHour to endHour do begin
if i > 12 then                   // змінює  і перевіряє циклічну змінну
if i = 24 then                   // якщо змінна циклу = 24
summa:= summa + 12               //кількість ударів годинника збільшується на 12
else
summa := summa + i mod 12       //до суми додається остача від ділення циклічної змінної на 12    ???????
else
summa := summa + i;             //якщо i>12 і i<24 то просто обчислюємо кількість ударів 
end;
Writeln(summa);                 // виводиться результат
end.
 Програма дещо складніша для розуміння ніж мій варіант. Потібно замітити, що учень також не використовує хвилини, прекрасно володіє умовним оператором і зумів в одному циклі перевірити всі умови і обчислити кількість ударів годинника. Програма проходить всі тести і набирає максимальну кількість балів.
 Семенюк Павла Рожнівський НВК    11 клас
program clock;
var h1,h2,m1,m2,c,k,i:integer;     //оголошення змінних
begin
writeln(‘введіть проміжки часу’);  // ввід початкових даних
readln(h1,m1);
readln(h2,m2);
c:=0;                               // знову непотрібна команда
if h2<h1 then h2:=h2+24;      //якщо наступний час менший за попередній додає 24 год
for i:=h1+1 to h2 do                // запускає цикл
begin
K:=i;                        // передає значення циклічної змінної і змінній k
if (i>12) and (i<=24) then k:=i-12; // перевіряє умови, робить висновки
if i>24 then k:=i-24;
c:=c+k;                              // збільшує суму ударів
end;
writeln(c);
end.
 Учениця продемострувала оригінальну манеру програмування. Програма дещо схожа на попередню, але значно коротша. У ній також не використовуються хвилини. Змінну k можна було і не вводити, а зразу писати:
if (i>12) and (i<=24) then с:=с+i-12; // перевіряє умови, робить висновки
За час відведений на цю задачу учні не встигають оптимізувати код. Після невеликої оптимізації код програми стане ще коротший і буде працювати швидше.
Загальний висновок: учасники олімпіади молодці і вчителі їхні також.
 Наступна задача
  1. Зелена пляма (20 балів).
    Два прожектори утворюють на стіні дві плями квадратної форми, одну – блакитного, а другу – жовтого кольору. Визначити площу плями зеленого кольору, яка утворилася внаслідок накладання двох плям – блакитної та жовтої.
 Вхідні дані: ви вводите з клавіатури у першому рядку координати лівої нижньої вершини кожного квадрата (натуральні числа x1,y1, х2, у2 <1000, розділені пропуском), у другому рядку – довжини сторін квадратів (натуральні числа а1, а2<100, розділені пропуском).
Вихідні дані: ви виводите на екран одне число – площу.
zelena

Уважно читаючи та аналізуючи задачу можна зробити такі логічні висновки:
  1. Плями можуть бути різних розмірів
  2. Плями можуть частково накладатися одна на одну з різних сторін
  3. Плями можуть НЕ накладатися одна на одну

Деякі види отриманих зображень.
плями2


Варіантів повного, чи часткового накладання, або не накладання я нарахував приблизно 25 і ще 25 якщо поміняти місцями кольори. Програма повинна передбачити всі можливі варіанти і виводити на екран площу зеленої плями. Якщо в програмі описати всі випадки то отримаємо надзвичайно довгий і заплутаний код. 
Хто має математичне мислення той може зануритися в обчислення на будь-яку глибину.
Я пішов іншим шляхом. Потрібно порівнювати по черзі координати всіх точок двох квадратів. Якщо координати точок співпадають – то фігури накладені одна на одну. Підраховуємо кількість спільних точок по ширині і по висоті.  Якщо ширина=0, або висота= 0 то і площа=0 – фігури не накладаються.
Для реалізації задумки вкладаємо один цикл в інший. В першому циклі беремо по черзі точки першого квадрату, вкладений цикл оперує точками другого квадрату. Кольори нас не цікавлять. Вкладені цикли використовуємо двічі і на виході отримаємо кількість співпадань по ширині і по висоті.
Шляхом неймовірних розумових зусиль я сотворив таку програму:
Program Zeleny_kvadrat;
var
a,b,x1,y1,x2,y2,a1,a2,i,j: integer; //оголошення змінних
label start ;                       // оголошення мітки початку програми 
begin
start:                              // мітка початку програми
a:=0;b:=0;                          // початкові значення ширини і висоти зеленої плями
writeln (‘Введіть координати квадратів через пробіл’);
readln(x1,y1,x2,y2);
writeln (‘Введіть розміри квадратів через пробіл’);
readln(a1,a2);
for i:=x1 to x1+a1-1 do begin    // змінюємо і від початкової координати до ширини першої плями
for j:=x2 to x2+a2-1 do          // змінюємо j від початку до ширини другої плями
if i=j then inc(a);          // якщо точки співпадають – збільшуємо ширину зеленої плями на 1 
end;
for i:=y1 to y1+a1-1 do begin    //аналогічно вчиняємо з висотою зеленої плями
for j:=y2 to y2+a2-1 do
if i=j then inc(b);              //в разі співпадання висота збільшується на 1 
end;
writeln (‘ площа зеленої плями ‘,a*b);
goto start;                      // перехід на початок
end.

Трохи поясню деякі частини програми:

 inc(a)це те саме що a:=a+1;
Якщо тіло вкладеного циклу складається з одного аператора(в нашому випадку if then) то Begin End писати не потрібно.
 З усіх учасників олімпіади майже справився з задачею Кричун Максим. Крім останнього програма проходить всі тести. Нижче представлений довжелезний код його творіння. Спробуйте його зрозуміти бо я не можу. Частково я пояснив деякі моменти цього чуда програмістики, а за решту вибачайте. Телефонуйте до автора і хай опише вам хід своїх геніальних думок.
program zadacha2;
Var
x1, y1:integer;              // Оголошено величезну кількість змінних
x2, y2:integer;
minX, minY:integer;
maxX, maxY:integer;
minA, maxA:integer;
minimumY, maximumY: integer;
minimumA, maximumA: integer;
a1,a2:integer;
S:integer;
begin
S := 0;                       // любитель зайвих команд (ця звичка знадобиться в мові С++)
Readln(x1,y1,x2,y2);          //читаємо з клавіатури вхідні дані
Readln(a1,a2);
if( x1 < x2) then begin       // Якщо другий квадрат лівіше першого то
maxX:= x2;
maxY:= y2;
minX:= x1;
minY:= y1;
minA:=a1;
maxA:=a2; end
else begin                    // якщо правіше то
maxX:= x1;
maxY:= y1;
minX:= x2;
minY:= y2;
minA:=a2;
maxA:=a1; end;
if( y1 < y2 ) then begin     // якщо другий квадрат вище першого то
minimumY:=y1;
maximumY:=y2;
minimumA:=y1; end
else begin                   // якщо нижче то
minimumY:=y2;
maximumY:=y1;
minimumA:=y2; end; //далі починається біла магія
if(minX + minA > maxX) and ( minimumY + minimumA > maximumY) then begin
if(minY + minA > maxY ) then begin  // заплутана, але діюча система умов і висновків
S:= (minX + minA – maxX) * (minY + minA – maxY);
end;
if(minY > maxY) then begin
S:=S – (minX + minA – maxX)* (minY – maxY);// обчислення плоші зеленого квадрату
end;
if(maxY + maxA < minY + minA) then begin //ще раз обчислюємо площу (якщо з першого разу не вийшло)
S:=S – (minX + minA – maxX)* ( minY + minA – (maxY + maxA));
end;
end;
Writeln(S);   // і нарешті вивід результату на екран
end.
Розумна людина знайде вихід з будь якої складної ситуації. Мудра людина ніколи не попаде в складну ситуацію
Якщо він сам розуміє те що понаписував то честь йому і хвала.
Перевірку  програм здійснюємо за такою тестовою таблицею.
 Тести до задачі «Зелена пляма»
Тест №1:
Вхід: 1 1 3 5             
        2 2                          Вихід: 0
 Тест №2:
     Вхід: 3 5 4 3             
        2 3                          Вихід: 1
 Тест №3:
Вхід: 1 1 1 1             
        9 2                          Вихід: 4
 Тест №4:
     Вхід: 300 798 300 700             
        99 99                      Вихід: 99
 Тест №5:
     Вхід: 800 800 810 810             
        99 10                      Вихід: 100


  1. Номери кімнат (25 балів)
Кімнати в готелі нумеруються натуральними числами починаючи з 1 (послідовно і без пропусків). Столяр готелю отримав завдання поміняти номерки на дверях кімнат на нові. Щоб зекономити час і зусилля він вирішив виготовити необхідні йому цифри із заготовок, які можна купити. Але переплачувати за зайву кількість заготовок він теж не хоче. Допоможіть столяру підрахувати скільки заготовок для цифр йому знадобиться?
Вхідні дані. У вхідному файлі З.in міститься одне натуральне число N — кількість кімнат у готелі (1 <N<5000).
Вихідні дані. У вихідний файлі 3.out потрібно записати одне натуральне число — необхідну кількість заготовок.
Приклади вхідних і вихідних даних:
столяр


Якщо той столяр не знає інформатики то нещасна його година. Нам простіше бо ми великі програмісти.
Хід думок:
  1. Третя задача має бути складніша за першу і за другу.
  2. Потрібно розкласти числа на цифри і підрахувати їх кількість.
  3. Створити масив заповнити цифрами і працювати з комірками масиву
  4. Перетворити число в рядок і працювати з рядковими величинами
Думки скачуть як покусані коні. Але всі думки виявилися неправильними. Все стане на місце коли написати ряд чисел, уважно на них подивитися і знайти закономірність.
1  2 3 4  5  6  7  8 9 10 11 12 13 14 15 16 17 18 19 20 21 22…99 100 101…999 1000 1001
1   2   3   4   5  6   7   8   9   11    13    15    17    19   21    23    25          
        +1                                               +2                                                                                                                  +3                              +4
 Не озброїним оком видно:
  • до 10 кількість заготовок збільшується на 1;
  • від 10 до 99 кількість заготовок збільшується на 2;
  • від 100 збільшується на 3.
Скільки цифр містить число стільки заготовок потрібно додати.
Потрібно організувати цикл від 1 до кількості кімнат, накласти декілька умов і підрахувати кількість заготовок. 
За умовою задачі – вхідні дані повинні читатися з файлу і результат повинен записуватися до файлу.
Program Hotel;
var N,K,i:integer; 
f:text;                    // Оголошуємо файлову змінну
begin
assignfile(f,’3.in’);      // прив’язуємо файлову змінну до вхідного файлу
reset(f);                  // відкриваємо файл для читання
readln(f,n);               // читаємо з файлу
close(f);                  // закриваємо файл(щоб з нього не пропала інформація)
For i:=1 to n do begin     // перераховуємо всі номери кімнат
if i<10 then k:=k+1;      // якщо номер менший за 10 то кількість заготовок збільшуємо на 1
if (i>=10)and(i<=99) then k:=k+2;    // від 10 до 99 збільшуємо на 2
if (i>=100)and(i<=999) then k:=k+3;  // від 100 до 999 збільшуємо на 3
if (i>=1000)and(i<=9999)then k:=k+4; // від 1000 до 9999 збільшуємо на 4
end;
assignfile(f,’3.out’);  //привязуємося до вихідного файлу
rewrite(f);             //відкриваємо файл для запису
write(f,k);             //записуємо до файлу
close(f);               //закриваємо файл
end.
  Все геніальне – просте, але, нажаль, не все просте є геніальним. Задача виявилася простішою за всі попередні. Читання з файлу і запис результату до файлу зайняли більшу частину програми.
Тестуємо програму за такою таблицею
готель

 4 Будинок лісника (40 балів)

Лісник Микола вирішив побудувати в лісі будинок. Він дуже любить природу, тому вирішив будувати його так, щоб не довелося зрубувати дерева. А будинок він хоче якомога більший і обов’язково квадратної форми. Микола вибрав прямокутну ділянку лісу і поділив її на умовні квадрати. Зайняті деревами квадрати він позначив 0 а вільні – 1. В результаті він отримав прямокутний масив, який містить М рядків по N цифр 0 або 1 в кожному (1 <= М, N <= 100). Допоможіть ліснику знайти максимально можливу площу квадратного будинку, який можна розмістити на умовних квадратах, позначених 1. Стіни будинку мають бути паралельні сторонам прямокутної ділянки.
Вхідні дані. Перший рядок вхідного файлу 4.in містить число М – кількість стовпчиків прямокутної ділянки. Другий рядок вхідного файлу 4.іn містить число N – кількість рядків прямокутної ділянки. Наступні N рядків файлу містять по М цифр: 0 або 1.
Вихідні дані. У вихідний файлі 4.out потрібно записати одне натуральне число – максимально можливу площу квадратного будинку, або нуль, якщо будинок не можна побудувати взагалі.
Примітка: площа умовних квадратів, на які поділена ділянка рівна 1. Приклади вхідних і вихідних даних:
4.іn
30
20
000000001000000000000000000000
000001111111111100000000000000
000000111111110100000000000000
000000111111110000000000000000
000000111111110000000000000000
000001111111110000000000000000
000011111111110000000000000000
000111111111110000000000000000
001111111111110000000000000000
000000000000000000000000000000
000000000000000011111111110000
000000000000000011111111110000
000000000000000011111111110000
000000000000000011111111110000
000000000000000011111111110000
000000000000000011111111110000
000000000000000011111111110000
000000000000000000000000000000
000000000000000000000000000000
000000000000000000000000000000
 
4.out
64
навіть не знаю з чого почати……думаю
вони би ще запитали скільки років бабусі того лісника….
відчуваю, що простого розв’язку тут немає і потрібно влаштовувати танці з бубном, одним словом без шаманства не обійдеться
пройшло 24 год   …      Є концепція програми.
Будинок має бути квадратний це значно спрощує програму
всі нулі і одиниці зчитуємо з файлу в масив і далі працюємо з масивом
здійснюємо пошук одиниць перевіряємо чи знаходиться знайдена одиниця в куточку квадрата таким чином:
перевіряємо чи справа і знизу є одиниці (це все робить окрема процедура)
якщо такі одиниці є  то перевіримо чи створюють вони квадрат, якщо так то
шукаємо одиниці ще нижче і ще правіше таким чином знайдемо величину крадрату і оголосимо її максимальною поки не знайдеться ще більший квадрат.
Залишилося тільки реалізувати це в програмі.       Тупікова концепція!!!!!!!
три дні мучив Паскаль – мертвий номер.
Придумав іншу концепцію
Використав Delphi XE. Текст програми такий:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Grids;
type
TForm1 = class(TForm)
Button1: TButton;
Memo2: TMemo;
Label2: TLabel;
SG: TStringGrid;
Edit1: TEdit;
Edit2: TEdit;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
n,m,s,smax,k,l,v,i,j:integer;
f:textfile;
d:char;
implementation
{$R *.dfm}
procedure pl; //процедура рахує площу квадрату
begin
v:=1; s:=0; //обнуляємо сторону квадрату і площу
// ідея така:
// якщо квадрат з одиниць то сума тих одиниць = площі квадрату
while v < m+1 do begin //поступово збільшуємо сторону квадрату
for k := j to j+v-1 do //переглядаємо символи в квадраті
for l := i to i+v-1 do begin
if form1.SG.Cells[k, l]=’1′ then //якщо в комірці “1”
s:=s+strtoint(form1.SG.Cells[k, l])// то додаємо до суми
else exit // якщо ні то вихід
end;
inc(v); //збільшуємо сторону квадрату на 1
if s>smax then begin smax:=s; s:=0; end; //порівнюємо з максимальною сумою
s:=0; //обнулюємо суму для наступних обчислень
end;
end;
procedure TForm1.Button1Click(Sender: TObject); //кнопка виконати
begin
memo2.Clear; //очищуємо табло з результатом
smax:=0; //максимальна сума на початку =0
assignfile(f,’4.in’);
reset(f); // читаємо вхідний файд
readln(f,m);
edit1.Text:=inttostr(m); //виводимо на екран вхідні дані
readln(f,n);
edit2.Text:=inttostr(n);
sg.RowCount:=n+2;
sg.ColCount:=m+2;
for i := 1 to n do //заповнюємо таблицю
for j := 1 to m+2 do
begin
read(f,d);
//текстові файли містять невидимі символи
//в таблицю пишемо тільки нулі і одиниці
if (d=’0′) or (d=’1′) then SG.Cells[j, i]:=d ;
end;
closefile(f);//закриваємо файл
//далі переглядаємо комірки таблиці у пошуках ‘1’
for i := 1 to n do
for j := 1 to m do
if SG.Cells[j, i]=’1′ then pl;//перехід на процедуру обчислення площі квадрата
memo2.Lines.Add(inttostr(smax)); //вивід результату на екран
memo2.Lines.SaveToFile(‘4.out’); //запис результату до файлу
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
memo2.Clear; //очистка табло з результатом
end; end.
Мушу трохи пояснити. На форму кидаємо такі компоненти: Кнопка Button, текстовий рядок Edit, мітку Label, таблицю StringGrid
02.01
Змінюємо розміри комірок таблиці і написи на інших компонентах у інспекторі об’єктів.
02.2201
Також змінюємо ім’я таблиці (Name)- пишемо SG. Так простіше використовувати його в програмі.
Уважно копіюйте текст програми бо частину коду Дельфі пише саме. Не дублюйте команди бо програма не запрацює.
Тут архів з всіма готовими проектами 
Хто зумів розв’язати ці задачі іншим, простішим способом – пишіть відгук, або кидайте на адресу SLKuty@meta.ua. Виставлю на сторінку з посиланням на автора. 
Не знаю якою має бути дитина щоб за 4 години розв’язати ці всі задачі. Можливо десь є такі діти, але я таких не бачив.

Немає коментарів:

Дописати коментар