Беркунський Збiрник Задач Пiдвищеноi Складностi

56
МІНІСТЕРСТВО ОСВІТИ УКРАЇНИ УПРАВЛІННЯ ОСВІТИ ОБЛДЕРЖАДМІНІСТРАЦІЇ ОБЛАСНИЙ ІНСТИТУТ УДОСКОНАЛЕННЯ ВЧИТЕЛІВ Є.Ю.Беркунський Збірник задач підвищеної складності з інформатики (з аналізом та розв’язком)

description

Сборник задач из далекого 2000 года

Transcript of Беркунський Збiрник Задач Пiдвищеноi Складностi

Page 1: Беркунський Збiрник Задач Пiдвищеноi Складностi

МІНІСТЕРСТВО ОСВІТИ УКРАЇНИУПРАВЛІННЯ ОСВІТИ ОБЛДЕРЖАДМІНІСТРАЦІЇ

ОБЛАСНИЙ ІНСТИТУТ УДОСКОНАЛЕННЯ ВЧИТЕЛІВ

Є.Ю.Беркунський

Збірник задач підвищеної складності з інформатики

(з аналізом та розв’язком)

Миколаїв 1998

Page 2: Беркунський Збiрник Задач Пiдвищеноi Складностi

Автор:

Рецензенти:

Є.Ю. Беркунський, методист інформаційно-комп’ютерного центру обласного інституту удосконалення вчителів.

О.М. Чадаєв, кандидат фізико-математичних наук, доцент кафедри математичного аналізу Миколаївського державного педагогічного інститутуО.В.Ткаченко, завідуюча інформаційно-комп’ютерного центру обласного інституту удосконалення вчителів.

У збірнику розглядаються задачі підвищеної складності з інформатики. Задачі поділені на розділи, в кожному розділі розглядається певний клас задач, та методи їх розв’язання. Частина задач наведена разом з повними розв’язками, частина – з ідеями розв’язку і деяка кількість задач наведена без розв’язків, бо їх розв’язання логічно випливає з розв’язання попередніх задач.

Друкується відповідно до рішення науково-методичної Ради (протокол № 17 від 22.12.1998 року).

2

Page 3: Беркунський Збiрник Задач Пiдвищеноi Складностi

Передмова

Ця робота написана за матеріалами роботи обласного факультативу з інформатики для обдарованих учнів, заняття якого автору довелось проводити з 1995 року. Для складання програм при розборі та розв‘язанні задач використовується мова програмування PASCAL у версії Turbo Pascal 7.0 фірми Borland International (далі - TP7). Проте, хоча в деяких програмах і використовуються можливості TP7, всі програми можуть бути виконані (можливо після невеличких виправлень, про які буде помічено в коментарях до відповідних програм) в середовищі Turbo Pascal версії 3.0 та вище.

Тут не будуть розглядатися проблеми візуального та об’єктно-орієнтованого програмування. Не розглядатимуться також проблеми прикладного програмування. А такий важливий розділ при вивченні програмування, як чисельні методи, буде зачеплений лише настільки, щоб був зрозумілим авторський розв‘язок задачі.

В цьому посібнику не будуть викладатися також і основи програмування та елементи мови Pascal. Для вивчення цих основ є дуже багато літератури, з якої можна порадити наприклад [1],[2],[3].

Ця робота може стати в нагоді тим вчителям інформатики, які бажають навчити учнів (і навчитися самим!) розв‘язувати задачі так званого «олімпіадного» характеру, тобто, за іншою термінологією – задачі підвищеної складності.

В одній з своїх робот Хоар писав, що естетична краса програми – це не архітектурне перебільшення, а те, що відрізняє в програмуванні успіх від невдачі. Якщо, при розв’язанні задач з цієї роботи читач відчує красу добре написаної програми, в якій "ані убавити, ані додати", та сумніви в правильності якої здаються безглуздими, то автор буде вважати, що його мета досягнута.

Рівень складності задач і розділів дуже різний. Автор намагався включити як прості задачі, які можуть бути корисними для початківців, так і важкі задачі, які можуть «посадити» навіть сильного школяра. (Хоча і рідко, але це буває корисно.)

Більшість задач наведені разом з розв’язками, проте деякі задачі наведені без розв’язків тому що їх розв’язок природно випливає з розв’язку попередньої задачі. При наведенні розв’язків автор притримувався ідеї наведення саме головного фрагмента програми, тобто того фрагменту, який розв’язує поставлену задачу. При цьому в більшій частині фрагментів пропущено розділ описань даних а також оператори введення та виведення. (Це не підручник з основ програмування!)

3

Page 4: Беркунський Збiрник Задач Пiдвищеноi Складностi

Задачі подаються за розділами:1. Задачі, що не використовують масиви. 2. Масиви.3. Сортування масивів.4. Утворення комбінаторних об’єктів.5. Задачі перебору варіантів.

Свої пропозиції щодо змісту видання, його форми подання просимо надсилати за адресою: м. Миколаїв, вул. Адміральська, 4, ІУВ, інформаційно-комп’ютерний центр.

4

Page 5: Беркунський Збiрник Задач Пiдвищеноi Складностi

Розділ 1. Задачі, що не використовують масивів.

Задача 1.1. Дано дві цілі змінні a, b. Скласти фрагмент програми, після виконання якого значення змінних обмінялись місцями (нове значення a дорівнювало старому значенню b та навпаки).

Розв’язок. Введемо додаткову цілу змінну t. t := a; a := b; b := t; Спроба обійтись без додаткової змінної, написавши a := b; b := a; не приводить до цілі (втрачається початкове значення змінної a).

Задача 1.2. Розв’язати попередню задачу, не використовуючи додаткових змінних (вважати, що значеннями цілих змінних можуть бути будь-які цілі числа).

Розв’язок. (Початкові значення a та b позначимо a0, b0.) a := a + b; {a = a0 + b0, b = b0} b := a - b; {a = a0 + b0, b = a0} a := a - b; {a = b0, b = a0}

Задача 1.3. Дано ціле число а та натуральне число n. Обчислити а в степені n. Іншими словами, треба скласти програму, при виконанні якої значення змінних а та n не змінюються, а значення деякої іншої змінної (наприклад, b) стає рівним а в степені n. (При цьому дозволяється використовувати і інші змінні.) Розв’язок. Введемо цілу змінну k, яка змінюється від 0 до n, при чому підтримується така властивість: b = (a в степені k).

k := 0; b := 1; {b = a в степені k} while k <> n do begin k := k + 1; b := b * a; end;

Інший розв’язок цієї ж задачі:

k := n; b := 1;

5

Page 6: Беркунський Збiрник Задач Пiдвищеноi Складностi

{a в степені n = b * (a в степені k)} while k <> 0 do begin k := k - 1; b := b * a; end;

Задача 1.4. Розв’язати попередню задачу, якщо вимагається, щоб кількість дій (операторів присвоєння) була порядку log n (тобто не перевищувало б C*log n для деякої константи C; log n - це степінь, до якої треба піднести 2, для того щоб отримати n).

Розв’язок. Внесемо деякі зміни в друге з запропонованих рішень попередньої задачі:

k := n; b := 1; c:=a; {a в степені n = b * (c в степені k)} while k <> 0 do begin if k mod 2 = 0 then begin k:= k div 2; c:= c*c; end else begin k := k - 1; b := b * c; end; end;

Кожного другого разу (не менше) буде виконуватися перший варіант умовного оператору (якщо k непарне, то після віднімання одиниці воно перетворюється на парне), таким чином за два цикли величина k зменшується щонайменше вдвічі.

Задача 1.5. Дано два натуральні числа a та b, які не дорівнюють нулю одночасно. Обчислити НСД (a,b) - найбільший спільний дільник а та b.

Розв’язок (1 варіант).

if a > b then begin k := a;

6

Page 7: Беркунський Збiрник Задач Пiдвищеноi Складностi

end else begin k := b; end; {k = max (a,b)}{інваріант: жодне число, більше k, не є спільним дільником}

while not (((a mod k)=0)and((b mod k)=0)) do begin k := k - 1; end; {k - спільний дільник, більших - немає}

(2 варіант - алгоритм Євкліду). Будемо вважати , що НСД(0,0) = 0. Тоді НСД(a,b) = НСД(a-b,b) = НСД(a,b-a); НСД(a,0) = НСД(0,a) = a для усіх a,b>=0.

m := a; n := b; {інваріант: НСД(a,b)=НСД(m,n); m,n >= 0 } while not ((m=0) or (n=0)) do if m >= n then m := m - n else n := n - m; if m = 0 then k := n else k := m;

Задача 1.6. Написати модифікований варіант алгоритму Євкліду, який використовує той факт, що для НСД(a,b) завжди має місце наступне співвідношення НСД(a,b) = НСД(a mod b, b) при a>=b, НСД (a, b) = НСД (a, b mod a) при b >= a.

Задача 1.7. Дано натуральні а та b, які не дорівнюють 0 одночасно. Знайти d = НСД (a,b) і такі цілі x та y, що буде виконуватися рівність d = a*x + b*y.

Розв’язок. Додамо в алгоритм Євкліду змінні p, q, r, s та впишемо в інваріант умови m=p*a+q*b; n=r*a+s*b.

m:= a; n:= b; p:= 1; q:= 0; r:= 0; s:= 1; {інваріант: НСД (a,b) = НОД (m,n); } {m,n >= 0 m = p*a + q*b; n = r*a + s*b.} while not ((m=0) or (n=0)) do if m >= n then begin m := m - n; p := p - r; q := q - s;

7

Page 8: Беркунський Збiрник Задач Пiдвищеноi Складностi

end else begin n := n - m; r := r - p; s := s - q; end; if m = 0 then begin k :=n; x := r; y := s; end else

begin k := m; x := p; y := q; end;

Задача 1.8. Скласти програму, яка друкує квадрати усіх натуральних чисел від 0 до вказаного натурального числа n.

Розв’язок.

k:=0; writeln (k*k); {Інваріант: k<=n, надруковані усі квадрати до k включно} while not (k=n) do begin k:=k+1; writeln (k*k); end;

Задача 1.9. Та сама задача, але дозволяється використовувати з арифметичних операцій лише додавання та віднімання, причому загальна кількість дій повинно бути порядку n.

Розв’язок. Введемо змінну k_square (square - квадрат), яка пов’язана з k співвідношенням k_square = k*k:

k := 0; k_square := 0; writeln (k_square); while not (k = n) do begin k := k + 1; {k_square = (k-1)*(k-1) = k*k-2*k + 1} k_square := k_square + k + k - 1; writeln (k_square); end;

8

Page 9: Беркунський Збiрник Задач Пiдвищеноi Складностi

Задача 1.10. Скласти програму, яка буде друкувати розклад на прості множники вказаного натурального числа n>0. (Іншими словами, треба надрукувати тільки прості числа і добуток надрукованих чисел повинен дорівнювати n, якщо n=1, друкувати нічого не треба).

Розв’язок (1 варіант).

k := n; {інваріант: добуток надрукованих чисел і k дорівнює n, на друковані лише прості числа} while not (k = 1) do begin l := 2; {інваріант: k не має дільників в інтервалі (1,l)} while k mod l <> 0 do

begin l := l + 1; end; {l - найменший дільник k, більший 1, тому він простий} writeln (l); k:=k div l; end;

(2 вариант).

k := n; L := 2; {добуток k і надрукованих чисел дорівнює равно n; надруковані числа прості; k не має дільників, менших L} while not (k = 1) do begin if k mod L = 0 then begin {k ділиться на L і не має дільників, менших L,тому, L просте} k := k div L; writeln (L); end else begin { k не ділиться на L } L := L + 1; end; end; Задача 1.11. Скласти програму розв’язку попередньої задачі, використовуючи той факт, що складене число має дільник, який не перевищує квадратного кореня з цього числа.

9

Page 10: Беркунський Збiрник Задач Пiдвищеноi Складностi

Розв’язок. В другому варіанті розв’язку замість L:=L+1 можна написати:

if L*L > k then L:=k else L:=L+1;

Задача 1.12. Дано натуральне число N. Визначити, чи є воно простим.

Задача 1.13. Дано натуральні числа n і k, n > 1. Надрукувати k десяткових знаків числа 1/n. (При існуванні двох десяткових представлень обирається те з них, яке не містить дев’ятки в періоді.) Програма повинна використовувати тільки цілі змінні.

Розв’язок. Після зсунення в десятковому запису числа 1/n коми на k місць праворуч, отримаємо число (10 в степені k)/n. Нам треба надрукувати його цілу частину, тобто поділити (10 в степені k) на n націло. Стандартний спосіб вимагає використання великих чисел, які можуть вийти за межі діапазону цілих чисел. Тому ми зробимо інакше (слідуючи звичайному методу "ділення кутом") і будемо зберігати "залишок" r:

L := 0; r := 1; {інваріант: надруковано L разрядів1/n, залишилось надрукувати k - L разрядів дробу r/n} while L <> k do begin write ( (10 * r) div n); r := (10 * r) mod n; L := L + 1; end;

10

Page 11: Беркунський Збiрник Задач Пiдвищеноi Складностi

Розділ 2. Масиви

В наступних задачах змінні x, y, z вважаються описаними як array [1..n] of integer (n - деяке натуральне число, більше 0), якщо інше не вказано явно.

Задача 2.1. Заповнити масив X нулями. (Тобто треба скласти фрагмент програми, після виконання якого всі значення x[1]..x[n] дорівнювали б нулю, незалежно від початкового значення змінної X.

Розв’язок.

i := 0; {інваріант: перші i значень x[1]..x[i] дорівнюють 0} while i < n do begin i := i + 1; {x[1]..x[i-1] = 0} x[i] := 0; end;

Задача 2.2. Порахувати кількість нулів в масиві x. (Скласти фрагмент програми, який не змінюючи значення x, запише в змінну k значення, яке дорівнювало б кількості нулів серед компонент масиву x.)

Розв’язок. ... {інваріант: k= число нулів серед x[1]...x[i] } ...

Задача 2.3. Знайти найбільший елемент з x[1]..x[n].

Розв’язок.

i := 1; max := x[1]; {інваріант: max = максимум з x[1]..x[i]} while i <> n do begin i := i + 1; {max = максимум з x[1]..x[i-1]} if x[i] > max then max := x[i]; end;

11

Page 12: Беркунський Збiрник Задач Пiдвищеноi Складностi

Задача 2.4. Дано масив x: array [1..n] of integer. Знайти кількість різних чисел серед елементів цього масиву. (Число дій повинно бути порядку n*n.)

Розв’язок.

{Впорядкуємо масив за неспаданням його елементів.}k := 1; i := 1;while i < n dobegin if X[i+1] <> X[i] then k := k+1; i := i+1end;

Задача 2.5. Та ж сама задача, але вимагається, щоб кількість дій була порядку n*log n. (Вказівка. Дивись розділ про сортування.)

Задача 2.6. Дано масив x[1]..x[n] цілих чисел. Не використовуючи інших масивів, переставити елементи масиву в зворотному порядку.

Розв’язок. Числа x[i] та x[n+1-i] треба поміняти місцями для усіх i, для яких

виконується умова: i < n + 1 - i, тобто 2 * i < n + 1, звідки – 2*i n, тому in div 2:

for i := 1 to n div 2 do begin ...обміняти x[i] і x[n+1-i]; end;

Задача 2.7. Дано масив цілих чисел x[1]..x[n], який будемо розглядати як з’єднання двох його відрізків: початку x[1]..x[m] довжини m та кінця x[m+1]..x[n] довжини n-m. Не використовуючи додаткових масивів, переставити початок і кінець. (Кількість дій повинна бути порядку n.)

Розв’язок. (1 варіант). Перевернемо (розташуємо в зворотному порядку) роздільно початок та кінець масиву, а потім перевернемо весь масив як одне ціле.

12

Page 13: Беркунський Збiрник Задач Пiдвищеноi Складностi

(2 варіант, автор – А.Г.Кушніренко) Розглядаючи масив записаним по колу, можна побачити, що потрібна дія – поворот кола. Як відомо з математики, поворот – це композиція двох осьових симетрій.

(3 варіант, автор - Ж.Арсак [4]). Цей метод дозволяє виконати вказане переміщення так, щоб кожний елемент пересувався тільки один раз, в той час, коли два попередніх вимагають, щонайменше двох переміщень. {Знаходимо d=НСД(m,n)} m1:=m;n1:=n; while m1<>n1 do if m1>n1 then m1:=m1-n1 else n1:=n1-m1; d:=m1;

{циклічно переставляємо елементи, які мають номери, що дають однакові залишки при діленні на n}

for j:=0 to d-1 do begin a:=X[j+1];k:=j+1; for i:=1 to n div d-1 do begin L:=(k+m-1) mod n+1; X[k]:=X[L]; k:=L end; X[k]:=a end;

Задача 2.8. (Двійковий пошук) Дано послідовність x[1]...x[n] цілих чисел та число a. З’ясувати, чи міститься a в цій послідовності, тобто чи існує i з 1..n, для якого x[i]=a. (Кількість дій повинно бути порядку log n.)

Розв’язок. (Вважаємо, що n > 0.)

L := 1; r := n+1;{Інваріант: якщо a міститься в масиві, то воно міститься серед x[l]..x[r-1], r > L} while r - L <> 1 do begin m := L + (r-L) div 2 ; {L < m < r } if x[m] <= a then begin L := m;

13

Page 14: Беркунський Збiрник Задач Пiдвищеноi Складностi

end else begin {x[m] > a} r := m; end; end;

(Зверніть увагу, що навіть у випадку x[m] = a інваріант не порушується) Кожного разу r-l зменшується приблизно вдвічі, звідки й випливає вказана оцінка кількості дій. Зауваження. L+(r-L)div 2 = (2L+(r-L))div 2 = (r+l)div 2.

Задача 2.9. Дано послідовність цілих чисел x[1],..., x[n]. Знайти її максимальну зростаючу підпослідовність. (кількість дій порядку n*log(n)).

Розв’язок.Будемо вважати, що вхідна послідовність пройдена до елемента з

номером i-1 включно. З’ясуємо, яку інформацію про пройдену частину треба мати, щоб розглянувши новий елемент з номером i можна було успішно продовжувати роботу так, щоб коли i буде дорівнювати N ми мали підпослідовність найбільшої довжини. Введемо означення «найкращої» підпослідовності даної довжини – з впорядкованих підпослідовностей довжини i «найкращою» будемо називати ту, яка має найменший останній елемент. Це означає, що така підпослідовність має найкращі шанси бути продовженою і дати в майбутньому «найкращу» послідовність більшої довжини. Таким чином, йдучи по масиву, нам треба будувати і в подальшому зберігати «найкращі» послідовності усіх можливих довжин. {Масив U[1..N,1..N] - масив найкращих послідовностей}{Підпослідовність з одного елемента - «найкраща»}{k - довжина найбільшої з «найкращих» послідовностей}U[1,1] := X[1]; k := 1;for i:=2 to N do {переглядаємо новий елемент}begin {Послідовність U[1,1],U[2,2],...,U[k,k] зростаюча, шукаємо в ній місце для X[i]} ...{двійковий пошук, див. Попередню задачу}

{результат – число j - місце для X[i]}U[j] := U[j-1]; U[j,j] := X[i];if j > k thenbegin k := j;end;

end;

14

Page 15: Беркунський Збiрник Задач Пiдвищеноi Складностi

Оцінка кількості дій випливає з того факту, що для кожного елемента (а їх – n) ми шукаємо місце в упорядкованій послідовності з n елементів, а для цього потрібно порядку log(n) дій, тому загальна кількість дій буде порядку n*log(n).

Задача 2.10. Дано послідовність, яка складається з N цілих чисел. Фрагментом цієї послідовності будемо вважати набір елементів, в якому індекси йдуть підряд. Знайти фрагмент з максимальною сумою, тобто знайти

такі індекси i та j, для яких величина iX iX jX jX 1 1. . . буде

найбільшою. Кількість дій в вашій програмі повинна бути порядку N.

Розв’язок.

d := 1; f := 1; m := X[1]; s := m; L :=2; k:=1;{Інваріант: m-найбільша сума фрагменту X[1]...X[L], S-сума фрагменту, що закінчується в X[L]} while L<=n do begin if s<0 then begin k:=L; s:=X[L] end else s:=s+X[i];

if s>m then begin d:=k; f:=L; m:=s end;

L:=L+1 end; i := d; j := f;

Таким чином, наведена програма лише один раз розглядає кожний елемент послідовності, тому, кількість дій порядку N.

Задача 2.11. В масиві містяться числа 0, 1 та 2. Переставити їх в порядку зростання.

Розв’язок.

Порахуємо кількість нулів - k0, кількість одиниць - k1 та кількість двійок - k2. Після цього заповним масив відповідним числом нулів, одиниць та двійок.

k0:=0; k1:=0; k2:=0;for i :=1 to n do

15

Page 16: Беркунський Збiрник Задач Пiдвищеноi Складностi

case X[i] of 0 : k0:=k0+1; 1 : k1:=k1+1; 2 : k2:=k2+1; end;for i:=1 to k0 do X[i]:=0;for i:=k0+1 to k0+k1 do X[i]:=1;for i:=k0+k1+1 to n do X[i]:=2;

Задача 2.12. Та сама задача, але за умови, що єдиною дозволеною операцією (крім читання) над масивом є перестановка двох його елементів.

Розв’язок.Для такого впорядкування необхідні три межі: до першої будуть йти

елементи, які дорівнюють 0, від першої до другої - 1, потім невідомо які до третьої, а після третьої - 2. Як черговий елемент будемо використовувати елемент, який є першим після другої межі.

l:=0; m:=0; r:=n; {інваріант: X[1..l]=0; X[l+1..m]=1; X[r+1]..X[n]=2} while m < r do begin if X[m+1]=1 then m:=m+1 else if X[m+1]=1 then begin обміняти(a[m+1],a[r]); r:=r-1; end else begin {a[m+1]=0} обміняти(a[m+1],a[l+1]); l:=l+1; m:=m+1; end; end;

Задача 2.13. Дано масив X та число b. Переставити елементи масиву так, щоб спочатку йшли елементи, що не більше b, а потім – елементи, які не менше b.

Задача 2.14. Та сама задача, але вимагається, щоб спочатку йшли елементи, які менше за b, потім – елементи, які дорівнюють b, а в кінці – елементи, які більше за b.

16

Page 17: Беркунський Збiрник Задач Пiдвищеноi Складностi

Розв’язок. Аналогічно розв’язку задачі 2.12. В задачі 2.13 повинні

використовуватися 2 межі, а в задачі 2.14 – 3 межі. Задача 2.15. Людина піднімається по сходах. На кожному кроку

вона може піднятися на один щабель, через один щабель, або через два щаблі. Скількома способами вона може піднятися на N щаблів? Скласти програму, яка це визначає.

Розв’язок.Очевидно, що на щабель з номером k можна потрапити лише з

щаблів з номерами k-3, k-2, k-1. Тому якщо S(i) – кількість способів потрапити на щабель з номером i, то S(k)=S(k-1)+S(k-2)+S(k-3).

Таким чином маємо:S[1]:=1;S[2]:=2;S[3]:=4;for i:=4 to N dobegin S[i]:=S[i-1]+S[i-2]+S[i-3]end;

Задача 2.16. Розв’язати ту ж саму задачу не використовуючи масив.

Розділ 3. Сортування масивів

В цьому розділі розглядаються стандартні методи сортування масивів та деякі їх удосконалення.

Дуже часто при розв’язанні деякої задачі на використання масивів, перед тим, як виконувати над масивом якусь дію (пошук, перетворення, додавання та вилучення елементів), масив треба спочатку впорядкувати (див., наприклад задачу 2.4).

Задача 3.1. Сортування обмінами (бульбашкове сортування). Цей метод полягає в тому що, послідовним переглядом X[1],X[2],...,X[n] знайти i таке, щоб X[i]>X[i+1]; поміняти X[i] та X[i+1] місцями, продовжити перегляд з елементу X[i+1] і т.д. Таким чином в наслідок першого перегляду найбільше число буде переміщене на останнє місце. Наступні перегляди знов починати з початку, зменшуючи на одиницю кількість елементів, що переглядаються. Масив буде впорядковано після перегляду, в якому брали участь тільки перший та другий елементи. Написати програму, яка реалізує сортування обмінами.

Розв’язок.

17

Page 18: Беркунський Збiрник Задач Пiдвищеноi Складностi

for i:=n-1 downto 1 do begin for j:=1 to i do if X[j]>X[j+1] then begin {Обміняти місцями X[i] та X[j]} R:=X[j]; X[j]:=X[j+1]; X[j+1]:=R end; end;

Задача 3.2. Сортування вибором. Очевидно, що у впорядкованому масиві перше місце буде займати найменший елемент, друге місце – найменший з усіх інших елементів і т.д. Нехай X[1],X[2],...,X[i-1] вже містять потрібні значення. Тоді визначення індексу k найменшого елементу з X[i],X[i+1],...,X[n] та обмін X[i] з X[k] приведуть до того, що вже X[1],X[2],...,X[i] матимуть потрібні значення. Після виконання таких дій для усіх i від 1 до n масив X буде впорядкованим.

Скласти програму сортування вибором.

Розв’язок.

for i := 1 to n-1 do begin k := i; for j := i+1 to n do if A[j]<A[k] then k := j; R:=A[i]; A[i]:=A[k]; A[k]:=R; end;

Задача 3.3. Сортування за допомогою вставок. Переглядаючи послідовно елементи X[2],X[3],...,X[n], кожний новий елемент X[i] будемо вставляти на відповідне місце в упорядковану вже послідовність X[1],...,X[i-1]. Це місце визначається послідовним порівнянням X[i] з впорядкованими елементами X[1],...,X[i-1].

Скласти програму сортування за допомогою вставок.Розв’язок.

Зауважимо, що в наведеній нижче програмі вважається, що масив X описано як array[0..n] of real, тобто в ньому, крім елементів X[1],...,X[n] , що підлягають сортуванню є елемент X[0], який ми будемо використовувати як

18

Page 19: Беркунський Збiрник Задач Пiдвищеноi Складностi

«межу» під час перегляду масиву в процесі пошуку місця для нового елемента.

for i:=2 to n dobegin X[0]:=X[i]; j:=i-1; while X[j]>X[0] do begin X[j+1]:=X[j]; j:=j-1; end; X[j+1]:=X[0];end;

Задача 3.4. Сортування бінарними вставками. Попередню програму можна змінити таким чином – місце, на яке треба вставити елемент X[i] в упорядкованій послідовності X[1],...,X[i-1], визначається за допомогою алгоритму двійкового пошуку (див. задачу 2.8).

Скласти програму, яка реалізує цей метод.

Попередні задачі цього розділу пропонували скласти програму одним з так званих прямих методів сортування.

В наступній таблиці наведено значення щодо ефективності прямих методів сортування. (С - кількість порівнянь, М - кількість обмінів).

Min Avg. MaxСортування вставками

C=n-1M=2(n-1)

C=(n2+n-2)/4M=(n2-9n-10)/4

C=(n2-n)/2-1M=(n2-3n-4)/2

Сортування вибором

C=(n2-n)/2M=3(n-1)

C=(n2-n)/2M=n(ln n+0.57)

C=(n2-n)/2M=n2/4+3(n-1)

Сортування обмінами

C=(n2-n)/2M=0

C=(n2-n)/2M=0.75(n2-n)

C=(n2-n)/2M=1.5(n2-n)

19

Page 20: Беркунський Збiрник Задач Пiдвищеноi Складностi

Крім прямих методів сортування, які ми щойно розглянули, було розроблено велику кількість так званих удосконалених методів сортування.

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

Задача 3.5. Дано два масиви x[1] <= ... <= x[k] та y[1]<=... <= y[L]. "Об’єднати" їх у масив z[1]<=...<= z[m] (m=k+L; кожний елемент повинен входити в масив z стільки разів, скільки він входить до масивів x та y разом). Кількість дій повинна бути порядку m.

Розв’язок.

k1 := 0; L1 := 0;{інваріант: результат буде отриманий, якщо до z[1]..z[k1+L1] приписати праворуч з’єднання масивів x[k1+1]..x[k] та y[l1+1]..y[L]}while (k1 <> k) or (L1 <> L) do begin if k1 = k then begin {L1 < L} L1 := L1 + 1; z[k1+L1] := y[L1]; end else if L1 = L then begin {k1 < k} k1 := k1 + 1; z[k1+L1] := x[k1]; end else if x[k1+1] <= y[L1+1] then begin k1 := k1 + 1; z[k1+L1] := x[k1]; end else if x[k1+1] >= y[L1+1] then begin L1 := L1 + 1; z[k1+L1] := y[L1]; end end;{k1 = k, l1 = l, масиви з’єднано} Цей процес можна пояснити так. Нехай ми маємо дві стопки карток, які впорядковані за алфавітом. Ми з’єднуємо їх в одну, вибираючи кожного разу ту з верхніх карток обох стопок, яка йде раніше за алфавітним порядком.

Тепер розглянемо два способи сортування , які використовують загальну ідею, що може бути корисною при побудові алгоритмів. За нею

20

Page 21: Беркунський Збiрник Задач Пiдвищеноi Складностi

закріпилась умовна назва «розділяй та володарюй» («divide et impera»), а зміст її полягає у тому, щоб задачу звести до декількох задач того ж типу, але меншого розміру. В нашому випадку ми зведемо сортування великого набору карток до сортування двох менших.

При цьому можливі два шляхи. При першому будемо поділяти купу карток, що підлягають

сортуванню, навпіл не дивлячись на те, що написано на картках. Після цього впорядкуємо кожну з відокремлених половин. Залишається лише з’єднати дві впорядковані частини, а це ми вже вміємо робити (див. попередню задачу).

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

Ідея другого такого метода сортування полягає у такому: ми можемо ліквідувати необхідність злиття двох впорядкованих куп карток, якщо заздалегідь будемо знати, що всі картки в першій купі йдуть раніше за алфавітом, ніж усі картки у другій купі. Таким чином ми отримаємо суттєву економію, намагаючись поділити картки так щоб вказана властивість виконувалася. Виконати це достатньо просто – треба вибрати одну з карток і використати її як межу – всі картки, що передують їй треба віднести до першої купи, а всі, що йдуть після неї – до другої. (Саму вибрану картку можна віднести до будь-якої з куп.)

В цьому способі є, проте, деяка небезпека. При невдалому збігу обставин може статися що купи, на які ми поділили вихідну купу будуть сильно відрізнятися за величиною (найгірший випадок – картка, яку ми вибрали як межу – є першою або останньою). Можна, однак, сподіватися що це буде траплятися рідко і що «в середньому» запропонований спосіб буде достатньо ефективним. Цей метод був запропонований Хоаром і отримав назву «QuickSort».

Задача 3.7. Скласти програму сортування за методом «QuickSort».

Розв’язок.

Як і раніше будемо вважати, що в програмі описано масив X, який складається з дійсних чисел. Наведемо процедуру, що буде впорядковувати цей масив за методом «QuickSort». (Повний варіант програми можна знайти серед прикладів програм, що є у комплекті поставки середовища Turbo(Bor-land) Pascal v.5.0-7.0.)

procedure Sort(L, R: Integer);var i, j, Cx, y: integer;

21

Page 22: Беркунський Збiрник Задач Пiдвищеноi Складностi

begin i := L; j := R; Cx := X[(L+R) DIV 2]; repeat while X[i] < Cx do i := i + 1; while Cx < X[j] do j := j - 1; if i <= j then begin y := X[i]; X[i] := X[j]; X[j] := y; i := i + 1; j := j - 1; end; until i > j; if L < j then Sort(L, j); if i < R then Sort(i, R);end;

Зазначимо, на завершення, що удосконалені методи сортування треба використовувати лише коли кількість елементів у масиві, що підлягає сортуванню дуже велика (порядку 1000), а в випадку невеликої кількості елементів можна використовувати більш прості прямі методи сортування.

22

Page 23: Беркунський Збiрник Задач Пiдвищеноi Складностi

Розділ 4. Утворення комбінаторних об’єктів

В цьому розділі зібрано задачі, в яких треба отримати один за одним усі елементи деякої множини.

4.1. Розміщення з повтореннями

Задача 4.1.1. Надрукувати усі послідовності довжини K, що складаються з чисел 1..n.

Розв’язок.

Будемо друкувати їх у лексикографічному порядку (послідовність а передує послідовності b, якщо для деякого s їх початкові відрізки довжини s рівні між собою, а (s+1)-ий член послідовності a менше). Першою буде послідовність <1, 1, ..., 1>, останньою - послідовність <n, n, ..., n>. Будемо зберігати останню надруковану послідовність у масиві x[1]...x[k].

...x[1]...x[k] присвоїти значення 1 ...надрукувати x ...last[1]...last[k] присвоїти значення n while x <> last do begin ...x := наступна за x послідовність ...надрукувати x end;

Опишемо, як можна перейти від x до наступної послідовності. Згідно визначенню, у наступної послідовності перші s членів повинні бути такими саме, а (s+1)-ий - більше. Це можливо, якщо x[s+1] було менше за n. Серед таких s треба вибрати найбільше (інакше отримана послідовність не буде безпосередньо наступною). Відповідне x[s+1] треба збільшити на 1. Таким чином, треба, рухаючись з кінця послідовності, знайти перший з правого боку член, який менше n (він знайдеться, так як за передумовою x<>last), збільшити його на 1, а елементи, що передують йому покласти рівними 1.

p:=k;while not (x[p] < n) do begin p := p-1;end;{x[p] < n, x[p+1] =...= x[k] = n}

23

Page 24: Беркунський Збiрник Задач Пiдвищеноi Складностi

x[p] := x[p] + 1;for i := p+1 to k do begin x[i]:=1;end;

Зауваження. Якщо членами послідовності вважати числа не від 1 до n, а від 0 до n-1, то перехід до наступного відповідає додаванню 1 в n-ічній системі числення.

Задача 4.1.2. В наведеному алгоритмі використовується порівняння двох масивів x <> last. Позбутися його, додавши булеву змінну L та додавши в інваріант відношення L<>послідовність x остання.

Задача 4.1.3. Надрукувати всі можливі підмножини множини {1...k}.

Розв’язок. Підмножини знаходяться у взаємно однозначній відповідності з послідовностями нулів і одиниць довжини k.

Задача 4.1.4. Надрукувати всі послідовності з k додатних цілих, у яких i-ий елемент не більше за i.

4.2. Перестановки

Задача 4.2.1. Надрукувати всі перестановки чисел 1..n (тобто послідовності довжини n, в які кожне з чисел 1..n входить по одному разу).

Розв’язок. Перестановки будемо зберігати в масиві x[1],..., x[n] та друкувати в лексикографічному порядку. (Першою при цьому буде перестановка <1 2...n>, останньою - <n...2 1>.) Для складання алгоритму переходу до наступної перестановки розглянемо таке питання: в якому випадку k-ий член перестановки можна збільшити, не змінюючи попередніх? Відповідь: якщо він менше будь-якого з наступних членів( членів з номерами більше ніж k). Ми повинні знайти найбільше k, при якому це так, тобто таке k, що x[k] < x[k+1] > ... > x[n]. Після цього x[k] треба збільшити мінімально можливим способом, тобто знайти серед x[k+1], ..., x[n] найменше число, яке більше нього. Помінявши x[k] з ним, залишається розмістити числа з номерами k+1,..., n так, щоб перестановка була найменшою, тобто в порядку зростання. Це полегшується тим, що вони вже розташовані в порядку спадання і залишилось лише переписати цю частину масиву в зворотному порядку.

Алгоритм переходу до наступної перестановки.

24

Page 25: Беркунський Збiрник Задач Пiдвищеноi Складностi

{Якщо виконується умова <x[1],...,x[n-1], x[n]> <n,...,2, 1>, тобто поточна послідовність не є останньою}k:=n-1;{послідовність праворуч від k спадаюча: x[k+1] >...> x[n]}while x[k] > x[k+1] do k:=k-1;{x[k] < x[k+1] > ... > x[n]}t:=k+1;{t <=n, x[k+1] > ... > x[t] > x[k]}while (t < n) and (x[t+1] > x[k]) do t:=t+1;{x[k+1]>...> x[t] > x[k] > x[t+1] >...> x[n]} ... обміняти x[k] з x[t]{x[k+1] > ... > x[n]} ... переставити елементи x[k+1] ... x[n] в зворотному порядку

Зауваження. Програма має такий дефект: якщо t = n, то значення x[t+1] не визначене, тому в загальному випадку не можна перевіряти умову (t<n)and(x[t+1]>x[k]), бо така перевірка може викликати вихід за межі масиву та аварійну зупинку програми, але Turbo Pascal 5 та більш нові версії коректно перевіряють умови такого типу, тобто якщо результат обчислення логічного виразу стає відомим ще до завершення обчислення за повною формулою, то подальші обчислення не проводитимуться. Тим хто має в своєму розпорядженні тільки більш ранні версії Паскалю, доведеться внести до програми певні зміни.

Задача 4.2.2. Змінити алгоритм переходу до наступної перестановки так, щоб він сам перевіряв, чи є ця перестановка останньою.

4.3. Підмножини

Задача 4.3.1. Перерахувати всі k-елементні підмножини множини {1..N}.

Розв’язок. Будемо подавати кожну підмножину послідовністю x[1]..x[n] нулів та одиниць довжини N, в якій точно k одиниць. (Інший спосіб подання розглянемо пізніше). Такі послідовності впорядкуємо у лексикографічному порядку (див. вище). Очевидний спосіб розв’язання задачі – перебирати усі послідовності як раніше, а потім відбирати серед них ті, у яких k одиниць ми відкинемо, вважаючи його нераціональним (кількість послідовностей з k одиницями може бути набагато менше кількості всіх послідовностей). Будемо шукати такий алгоритм, щоб отримання наступної послідовності вимагало порядку N дій.

25

Page 26: Беркунський Збiрник Задач Пiдвищеноi Складностi

В якому випадку s-й член послідовності можна збільшити, не змінюючи попередні? Якщо x[s] змінюється з 0 на 1, то для збереження загальної кількості одиниць треба праворуч від х[s] замінити 1 на 0. Таким чином, х[s] - перший справа нуль, за яким стоять одиниці. Легко бачити, що х[s+1] = 1 (інакше х[s] не перший). Таким чином треба шукати найбільше s, для якого х[s]=0, x[s+1]=1;

X 0 1...1 0...0S

За х[s+1] можуть йти ще декілька одиниць, а після них декілька

нулів. Замінивши х[s] на 1, треба вибрати елементи, що йдуть за ним так, щоб послідовність була б мінімальна з точки зору нашого порядку, тобто щоб спочатку йшли нулі, а потім одиниці. Таким чином, отримаємо:

перша послідовність 0...01...1 (n-k нулів, k одиниць) остання послідовність 1...10...0 (k одиниць, n-k нулів)

алгоритм переходу до наступної за х[1]...x[n] послідовності (вважаємо, що вона існує):

s := n - 1; while not ((x[s]=0) and (x[s+1]=1)) do begin s := s - 1; end; {s - елемент, що буде змінено з 0 на 1} num:=0; for k := s to n do begin num := num + x[k]; end; {num - кількість одиниць серед x[s]...x[n], число нулів дорівнює (довжина - кількість одиниць), тобто(n-s+1) - num} x[s]:=1; for k := s+1 to n-num+1 do begin x[k] := 0; end; for k := n-num+2 to n do begin x[k]:=1; end;

26

Page 27: Беркунський Збiрник Задач Пiдвищеноi Складностi

Інший спосіб подання підмножин – це перелік їх елементів. Щоб кожна підмножина мала рівно одне подання, домовимося перелічувати елементи в зростаючому порядку. Приходимо до такої задачі.

Задача 4.3.2. Перелічити всі зростаючі послідовності довжини k з чисел 1..n в лексикографічному порядку. (Приклад: при n=5, k=2 отримуємо 12 13 14 15 23 24 25 34 35 45.)

Розв’язок. Мінімальною буде послідовність 1, 2, ..., k; максимальною – (n-k+1),

..., (n-1), n. В якому випадку s-ий член послідовності можна збільшити? Відповідь: якщо він менше ніж n-k+s. Після збільшення s-го елементу всі наступні повинні зростати з кроком 1. Отримаємо такий алгоритм переходу до наступної послідовності: s:=n; while not (x[s] < n-k+s) do begin s:=s-1; end; {s - елемент, що підлягає збільшенню}; x[s] := x[s]+1; for i := s+1 to n do begin x[i] := x[i-1]+1; end;

Задача 4.3.3. Нехай ми вирішили подавати k-елементні підмножини множини {1..n} спадаючими послідовностями довжини k, які впорядковані як і раніше у лексикографічному порядку. (Приклад: 21 31 32 41 42 43 51 52 53 54.) Як буде виглядати тоді алгоритм переходу до наступної?

Розв’язок.Шукаємо найбільше s, для якого х[s]-x[s+1]>1. (Якщо такого s

немає, будемо вважати s = 0.) Збільшивши x[s+1] на 1, робимо інші мінімально можливими (x[t] = k+1-t для t>s).

Задача 4.3.4. Розв’язати дві попередні задачі, замінивши лексикографічний порядок на зворотній (раніше йдуть ті, які більше в лексикографічному порядку).

Задача 4.3.5. Перелічити всі вкладення (функції, що переводять різ-ні елементи в різні) множини {1..k} в {1..n} (вважається, що k <= n). Утворення чергового елементу повинно вимагати порядку k дій.

27

Page 28: Беркунський Збiрник Задач Пiдвищеноi Складностi

Вказівка. Ця задача може бути зведена до переліку підмножин та перестановок елементів кожної підмножини.

Задача 4.3.6. В романі N глав. В j-ій главі A[j] сторінок, j=1,2,...,N. Потрібно видати роман в K главах так, щоб обсяг найбільшого тому був якомога меншим. Ділити главу між томами та переставляти порядок глав не можна. Скласти програму, яка визначає оптимальний обсяг найбільшого тому та розподіл глав по томах.

Вказівка. Ця задача може бути розв’язана за допомогою перегляду всіх можливих розподілів глав по томах.

4.4. Розбиття

Задача 4.4.1. Перелічити всі розбиття цілого додатного числа N на цілі додатні складники (розбиття, що відрізняються лише порядком складників вважаються за одне). (Приклад: n=4, розбиття 1+1+1+1, 2+1+1, 2+2, 3+1, 4.)

Розв’язок. Домовимося, що (1) в розбиттях складники йдуть у незростаючому порядку, (2) самі розбиття ми перелічуємо в лексикографічному порядку. Розбиття зберігаємо у початку масиву x[1]...x[n], при цьому кількість чисел, що входять у нього позначимо k. На початку x[1]=...=x[n]=1, k=n, на прикінці x[1]=n, k=1.

В якому випадку x[s] можна збільшити не змінюючи попередніх? По-перше, повинно бути x[s-1] > x[s] або s = 1. По-друге, s повинно бути не останнім елементом (збільшення s треба компенсувати зменшенням наступних). Збільшивши s, усі наступні елементи треба взяти мінімально можливими.

s := k - 1; while not ((s=1) or (x[s-1] > x[s])) do begin s := s-1; end; {s - складник, що підлягає збільшенню} x [s] := x[s] + 1; sum := 0; for i := s+1 to k do begin sum := sum + x[i]; end; {sum - сума членів, що стоять після x[s]} for i := 1 to sum-1 do begin

28

Page 29: Беркунський Збiрник Задач Пiдвищеноi Складностi

x [s+i] := 1; end; k := s+sum-1;

Задача 4.4.2. Представляючи, як раніше, розбиття, як незростаючі послідовності, перелічити їх у порядку, зворотному ло лексикографічного (для n=4, наприклад, повинно бути отримано 4, 3+1, 2+2, 2+1+1, 1+1+1+1).

Вказівка. Зменшувати можна перший справа член, який не дорівнює 1; знайшовши його, зменшимо на 1, а наступні візьмемо максимально можливими (такими, що дорівнюватимуть йому, поки достатньо суми, а останній – скільки залишиться).

Задача 4.4.3. Представляючи розбиття, як незростаючі послідовності, перелічити їх у лексикографічному порядку. Приклад для n=4: 1+1+1+1, 1+1+2, 1+3, 2+2, 4.

Вказівка. Останній член збільшити не можна, а передостанній – можна; якщо після збільшення на 1 передостаннього члена за рахунок останнього буде порушено зростання, то з двох членів треба зробити один, якщо ні, то останній член треба розбити на складники, що дорівнюють попередньому та залишок, що не менше його.

Задача 4.4.4. Представляючи розбиття, як неспадаючі послідовності, перелічити їх у порядку, зворотному до лексикографічного. Приклад для n=4: 4, 2+2, 1+3, 1+1+2, 1+1+1+1.

Вказівка. Для того, щоб елемент x[s] можна було зменшити, необхі-дно, щоб s = 1 або x[s-1] < x[s]. Якщо x[s] не останній, то цього і достатньо. Якщо він останній, то треба, щоб x[s-1] <= (ціла частина (x[s]/2)) або s=1.

29

Page 30: Беркунський Збiрник Задач Пiдвищеноi Складностi

Розділ 5. Задачі перебору варіантів.

Програми, які розглядатимуться у цьому розділі є простими та наочними прикладами цілого класу алгоритмів, в яких розв’язок буде шукатися не прямо, а еврістично за допомогою метода проб та помилок. Цей метод полягає у тому, щоб за деяким правилом крок за кроком будувати можливі «умовні» рішення (кандидати), кожне з яких потім перевіряється на відповідність критеріям, що характеризують розв’язок. Якщо «умовне» рішення буде визнано як неприпустиме, то будується наступний кандидат; причому в цьому випадку можливо декілька попередніх кроків доведеться анулювати. Ми почнемо розглядання такого типу задач з задачі, що стала класичною.

Задача 5.1. Скласти програму, що будує всі конфігурації восьми ферзів на шахівниці з 8х8 полів за такої умови, що жоден з ферзів не може побити ніякого іншого. Іншими словами, в конфігураціях, що ми шукаємо, ніякі два ферзі не можуть знаходитися на одній горизонталі, вертикалі, чи діагоналі. [5]

Розв’язок. Розв’язок цієї задачі не будемо будувати безпосередньо, замість

цього складемо загальну схему щодо перебору варіантів. Будемо вважати, що нам треба побудувати процедуру, яка буде знаходити всі рішення цієї задачі. Процедура, яка виконує таке завдання повинна оперувати з поняттями глибини та ширини перебору. Глибина перебору, це мінімальна кількість операцій по вибору одного елемента розв’язку, яку треба виконати. Для задачі про ферзів глибина – це кількість ферзів, що треба поставити на дошку, тобто – 8. Ширина перебору, це більш складна характеристика і вона сильно залежить від реалізації методу перебору. Зазначимо лише, що складаючи програму перебору варіантів необхідно намагатися досягнути якнайменшої ширини перебору. Сформулюємо таку процедуру, що для цілого числа i визначає значення i-го кандидата. (i - поточна глибина перебору).

30

Page 31: Беркунський Збiрник Задач Пiдвищеноi Складностi

Загальна схема перебору варіантів

Procedure Try(i:integer);begin {Ініціалізація вибору кандидатів}; repeat

{вибрати наступного кандидата}; if {кандидат підходить} then

begin {Записати кандидата}; if {Розв’язок повний}

then {Обробка розв’язку} else Try(i+1) {шукати далі};

{Стерти кандидата}; end;

until {більше немає кандидатів}; end;

Пояснимо деякі моменти цієї схеми: процедуру ми назвали Try(i), тому, що вона намагається знайти значення для і- го кандидата (Try - спроба (англ.)). {Ініціалізація вибору кандидатів} – це деякі початкові дії, які треба виконати на початку вибору i-го кандидата. Далі треба послідовно переглядати усіх кандидатів. Для кожного кандидата треба визначити, чи підходить він для нашого розв’язку. Якщо підходить, то його треба «записати», після цього треба перевірити поточну глибину – якщо вона дорівнює повній глибині перебору, то знайдений частковий розв’язок є одним з повних розв’язків і його треба якимось чином обробити (наприклад надрукувати), якщо ні, то треба продовжити пошук кандидатів на більшій глибині перебору, що виконує оператор Try(i+1). Після цього треба стерти цього кандидата і шукати розв’язок з іншими кандидатами. Цей процес треба зупинити коли буде знайдено деяка кількість розв’язків (наприклад, один), або коли більше не буде кандидатів.

Тепер розглянемо, як можна зменшити ширину перебору. Очевидно, що якщо кожного ферзя можна поставити на одну з 64 кліток дошки, то такий перебор буде мати ширину 64 і загальна кількість варіантів буде 648. Звичайно, що таку кількість варіантів важко переглянути. Тому для того, щоб зменшити кількість варіантів, спочатку сформулюємо основні властивості нашого розв’язку (позиції, де 8 ферзів розташовані як треба).

a) Жодна з вертикалей не може містити більш ніж одного ферзя, треба розмістити 8 ферзів, і шахівниця містить 8 вертикалей. Тому, можна зробити висновок, що кожна вертикаль буде містити рівно одного ферзя.b) Аналогічно можна зробити висновок, що кожна горизонталь буде містити рівно одного ферзя.

31

Page 32: Беркунський Збiрник Задач Пiдвищеноi Складностi

c) Маємо 15 діагоналей, що «піднімаються», кожна з яких містить не більш ніж одного ферзя, тобто 8 діагоналей, що «піднімаються» містять по одному ферзю, а інші 7 – порожні.d) Аналогічно приходимо до висновку, що 8 діагоналей, що «спускаються» містять рівно по 1 ферзю і 7 є порожніми.e) Якщо задана деяка непорожня конфігурація ферзів, в якій жодна пара ферзів не може побити одне одного, то вилучення будь-якого з цих ферзів приведе до появи конфігурації, що зберігає цю властивість.Остання властивість є дуже важливою. Використаємо тут такий

прийом (див.[5]): почнемо з деякого розв’язку і будемо один за одним вилучати ферзів з дошки. Якщо цей процес зняти на кіноплівку, а потім «прокрутити» у зворотному напрямку, то можна побачити як порожня дошка поступово заповнюється додаваннями одиночних ферзів і як послідовне проходження конфігурацій з деякої множини (множини конфігурацій з N8 ферзями, з яких жодна пара не загрожує одне одному), приводить нас до даного розв’язку. При отриманні такої «кіноплівки» будь-який розв’язок можна зводити до порожньої дошки різними способами і рівно стільки ж способів побудови кожного розв’язку. З цієї свободи вибору нам важливо знайти спосіб методично перебирати всі варіанти так, щоб не пропустити жодного.

По-перше, нам треба знайти відповідь на питання, як ми будемо записувати знайдені розв’язки. Згідно властивості (a) кожна вертикаль містить рівно одного ферзя. Тому кожна конфігурація з 8 ферзів на шахівниці може бут описаною за допомогою масиву X : array[1..8] of inte-ger , де X[i] – це номер горизонталі, яку займає ферзь, який стоїть на вертикалі і.

Наступна проблема полягає у тому, як перевіряти умову, що клітка вільна (нам це необхідно для визначення, чи можна поставити туди нового ферзя). Як це можна зробити? Наприклад, можна представити собі масив з 8х8 елементів, які вказують для кожної клітки, чи є вона вільною. Якщо ми вводимо такий масив, то додавання одного ферзя може вплинути на значення 28 клітин. Проте вилучення ферзя при цьому виявляється достатньо складним процесом, тому що зовсім невідомо, що клітки, які були під ударом цього ферзя стають вільними взагалі – вони можуть бути під ударом ще деяких з ферзів, що залишились на дошці. Проти цього є засіб – можна вважати, що масив 8х8 містить лічильники, кожний з яких спочатку дорівнює нулю, а в подальшому буде вказувати скільки ферзів б’ють це поле. Але таким чином додавання чи вилучення ферзя з дошки буде супроводжуватися зміною значень до 28 клітин – а це дуже важка розплата за встановлення чи вилучення одного ферзя.

Кожного разу, коли ми намагаємося встановити чергового ферзя на деяку клітину, цій клітині відповідає вертикаль, яка вільна за означенням), відповідає горизонталь (яка повинна бути поки ще бути порожньою),

32

Page 33: Беркунський Збiрник Задач Пiдвищеноi Складностi

відповідає одна з 15 діагоналей, що «піднімаються» і одна з 15 діагоналей, що «спускаються» (які повинні бути поки ще порожніми). Це приводить до висновку, що нам треба слідкувати за

1) вільними горизонталями;2) вільними діагоналями, що «піднімаються»;3) вільними діагоналями, що «спускаються».Очевидно, що горизонталі можна відрізняти за їх номерами та їх

стан «вільності» зберігати у масиві A : array[1..8] of Boolean. А як відрізняти діагоналі? Для кожного поля вздовж діагоналі, що «піднімається» різниця між номером горизонталі і вертикалі на яких вона знаходиться є незмінною, а їх сума залишається незмінною вздовж кожної діагоналі, що «спускається». Тому стан діагоналей будемо зберігати у масивах B : array[-7..7] of Boolean для діагоналей, що «піднімаються», та C : array[2..16] of Boolean для діагоналей, що «спускаються». Таким чином, перевірку, чи є поле вільним можна записати так : A[k]andB[i-k]andC[i+k].

Таким чином, остаточно програма набуває такого вигляду:

Program QUEENS;var X:array[1..8] of integer; A:array[1..8] of boolean; B:array[-7..-7] of boolean; C:array[2..16] of boolean;procedure Q{обробка}; var i:integer; begin for i:=1 to N do begin write(X[i]:3); end; writeln;end;

procedure Try(i:integer); var k:integer; begin {ініціалізація вибору кандидатів} for k:=1 to 8 do begin {выбор наступного кандидата} if A[k] and B[i-k] and C[i+k]

{підходить} then begin {записати його} X[i]:=k;A[k]:=false;

33

Page 34: Беркунський Збiрник Задач Пiдвищеноi Складностi

B[i-k]:=false;C[i+k]:=false; if {розв’язок повний}i=8 then {обработка}Q else Try(i+1); {стерти кандидата} A[k]:=true;B[i-k]:=true;C[i+k]:=true; end end{більше немає кандидатів} end; begin for i:=1 to 8 do A[i]:=true; for i:=-7 to 7 do B[i]:=true; for i:=2 to 16 do C[i]:=true; Try(1);end.

Зауважимо, що цикл Repeat...until перетворився на цикл For... to ... do тому, що на кожному кроку заздалегідь було відомо, що кількість кандидатів однакова і становить 8.

Задача 5.2. Побудувати послідовність з N літер в алфавіті, який складається з трьох елементів (наприклад, 1, 2, 3) таку, щоб жодні дві сусідні підпослідовності не співпадали одна з одною.[2] Наприклад, послідовність довжини N=5 з літерами «12321» правильна, проте послідовності «12323» та «12123» – ні.

Розв’язок....................procedure Try(i:integer); var k:integer;begin {ініціалізація вибору кандидатів} for k:=1 to 3 do begin if { підходить } then begin

A[i]:=k;{Записати кандидата}if i=N{Розв’язок повний}

then Q{Обробка} else Try(i+1);{ Стерти кандидата }

end; end;end;

34

Page 35: Беркунський Збiрник Задач Пiдвищеноi Складностi

..................begin readln(N); try(1);end.В цьому фрагменті залишилося конкретизувати фрагменти, що

підкреслені. Цю частину роботи пропонується виконати самостійно.

Задача 5.3. «Рюкзак». Існує N предметів, кожний з яких має свою вагу та вартість. З заданих предметів треба вибрати такі, щоб їх загальна вага не перевищувала найбільшої припустимої ваги – M кілограмів, а вартість була якомога більшою. Надрукувати загальну вартість вибраних предметів.

Більш точно – задано два масиви додатних чисел A[1..N] та B[1..N]. Треба вибрати такі попарно різні числа i1, i2, ..., ik, так, щоб сума A[i1]+A[i2]+...+A[ik]M, а сума B[i1]+B[i2]+...+B[ik]=max була максимальною.

Розв’язок. З множини N предметів нам треба переглянути всі підмножини та

вибрати одну – «оптимальну». Це наводить на ідею скористатися програмою генерації усіх підмножин (див. задачу 4.1.3). При цьому кількість дій буде порядку 2N.

Інший шлях полягає в тому, що ці підмножини будемо будувати за загальною схемою перебору варіантів, при цьому глибина перебору буде кількість елементів у масиві, тобто N, а ширина – 2. Тобто на кожному кроку при побудові підмножини нам треба з’ясовувати, будемо чи не будемо брати i-й предмет. Таким чином, у схемі перебору зовсім зникає цикл, натомість з’являється розгалуження.

procedure Q;{Обробка}begin if OptStoim<Stoim then begin OptStoim:=Stoim; OptMnog:=Mnog; end;end;procedure Try(i:integer);begin if {можна брати предмет:} Ves+A[i]<=M then

35

Page 36: Беркунський Збiрник Задач Пiдвищеноi Складностi

begin {записати кандидата:} Ves:=Ves+A[i]; Stoim:=Stoim+B[i];

Mnog:=Mnog+[i]; if i=NN{Переглянули всі предмети} then Q {обробка} else Try(i+1); {стирання кандидату:}

Ves:=Ves-A[i]; Stoim:=Stoim-B[i]; Mnog:=Mnog-[i]; end; if {можна не брати предмет:} MaxStoim-B[i]>OptStoim then begin {записати кандидата:}

MaxStoim:=MaxStoim-B[i];

if i=NN {Переглянули всі предмети} then Q {обробка} else Try(i+1);

{стирання кандидату:} MaxStoim:=MaxStoim+B[i];

end;end;begin {введення даних} ...... {ініціалізація змінних} MaxStoim:=0;{Знаходимо загальну вартість предметів} for i:=1 to NN do begin MaxStoim:=MaxStoim+B[i]; end; Mnog:=[];OptMnog:=[];Stoim:=0; OptStoim:=0;Ves:=0; Try(1); writeln('Max варт=',OptStoim); end.

В цій програмі змінні Mnog, OptMnog описані, як set of 1..N. Змінна Stoim визначає поточну вартість набору, MaxStoim – поточну максимально досяжну вартість, тобто вартість, яку ще можна досягнути, якщо брати усі предмети, які ще не були розглянуті. Такий підхід, порівняно з підходом, що базується на перегляді усіх підмножин даної множини, дає можливість деякі підмножини, про які відомо, що вони є безперспективними, тобто не

36

Page 37: Беркунський Збiрник Задач Пiдвищеноi Складностi

приведуть до оптимальної підмножини не розглядати. Так, наприклад, якщо відомо, що взявши елементи з номерами i1, i2, i3 ми вже перебільшили максимально можливу вагу, то можна далі не розглядати ті підмножини предметів до яких входять усі ці предмети. З іншого боку, якщо, наприклад, не взявши до набору елементи з номерами i1, i2 ми не зможемо досягти вартості деякого набору, який був знайдений раніше, то можна не розглядати підмножини, що не мають у своєму складі цих елементів, бо вони не дадуть оптимальної підмножини. Такий метод є дуже поширеним і має назву «Метод гілок і меж» (Рос. «Метод ветвей и границ»).

Наведемо ще декілька задач, які розв’язуються за допомогою методу перебору варіантів.

Задача 5.4. Знайти послідовність з 50 нулів та одиниць, в якій жоден відрізок не повторюється три рази підряд. Надрукувати НІ, якщо такої послідовності не існує.

Наприклад, в цій послідовності не повинні зустрічатися такі відрізки, як 000, або 1010, або 101101101.

Розв’язок.

procedure Try(i:integer); var k:integer;begin

k:=-1;{Ініціалізація}repeat k:=k+1;{Перейти до наступного} if {підходить} then begin

{Записати кандидата} if i=50 {розв’язок повний}

then Q else Try(i+1);

{Стерти кандидата} end;until (k=1)or({розв’язок знайдено});

end;Примітка. Читачеві пропонується самостійно конкретизувати

вказівки, що замінені коментарями {підходить}, {Записати кандидата}, {Стерти кандидата} та {розв’язок знайдено}.

Задача 5.5. Дано натуральне число m та масив натуральних чисел A[1..N]. В послідовності A вибрати підпослідовність Ai1, Ai2, ..., Aik таку, що Ai1+Ai2+ ...,+Aik=m. Якщо таку підпослідовність вибрати неможливо то надрукувати повідомлення про це.

37

Page 38: Беркунський Збiрник Задач Пiдвищеноi Складностi

Задача 5.6. Дано N предметів, вага яких записана у масиві A[1..N]. Поділити ці предмети на дві групи так, щоб загальна вага одної групи якнайменше відрізнялась від загальної ваги іншої.

Задача 5.7. Знайти таку розстановку п’яти ферзів на шахівниці, при якій кожне поле буде знаходитись під ударом одного з них.

Задача 5.8. Знайти маршрут шахового коня по шахівниці яка має розмір NхN клітин, що по одному разу проходить по всіх клітках дошки.

Задача 5.9. Дано N міст. Деякі з них з’єднані дорогами відомої довжини. Вся система шляхів задана квадратною матрицею порядку N, елемент A[i,j], якої дорівнює деякому від’ємному числу, якщо місто i не з’єднано шляхом з містом j та дорівнює довжині шляху в іншому випадку (i, j = 1,2,...,N).a) Для 1-го міста знайти найкоротші маршрути в інші міста.b) Вважаючи, що кожне місто з’єднано напряму шляхом з кожним іншим,

знайти найкоротшій замкнутий маршрут, який починається в 1-му місті і проходить через усі інші міста.

Задача 5.10. Вас найняли для того, щоб визначити місця дипломатів за столом обговорень міжнародної конференції. На конференцію запрошені по одному дипломату з N різних країн світу. Кожен дипломат знає від однієї до M мов. Дипломати, які не знають спільної мови, не можуть розмовляти один з одним. До того ж, деякі країни проголосили, що не будуть підтримувати дипломатичних стосунків з деякими іншими, тобто представники цих країн не будуть розмовляти один з одним. Ваша задача полягає в розробці програми, що визначає місця за столом для дипломатів таким чином, щоб кожен міг розмовляти з обома своїми сусідами, які сидять ліворуч та праворуч від нього.

Стіл, що використовується, – круглий і розрахований на N персон. Дипломат може спілкуватись з дипломатом, який сидить ліворуч однією мовою, а з дипломатом, що сидить праворуч – іншою.

Задача 5.11. Дано натуральне число N. Будемо розглядати перестановки, що утворюються з чисел 1,2,3,…,N. Перестановку, в якій числа йдуть в порядку зростання назовемо «прямою», а перестановку, в якій числа йдуть в порядку спадання – «зворотною». Розглянемо деяку перестановку з чисел 1,2,3,…,N.

Виконаємо з нею такі дії:А) додамо кожне з чисел даної перестановки до відповідного йому

числа з «прямої» перестановки. В результаті отримаємо набір, що складається з N чисел.

38

Page 39: Беркунський Збiрник Задач Пiдвищеноi Складностi

B) додамо кожне з чисел даної перестановки до відповідного йому числа з «зворотної» перестановки. В результаті отримаємо набір, що складається з N чисел.

Якщо в результаті вказаних дій отримано два набори, в кожному з яких всі елементи різні, то дану перестановку будемо називати «особливою».

Наприклад, для N=4.Перестановка 2,4,1,3 - «особлива», тому щоA) 2 4 1 3 1 2 3 4 3 6 4 7 - всі числа різні

B)2 4 1 3 4 3 2 1 6 7 3 4 - всі числа різні

Перестановка 1,4,2,3 - не є « особливою » , тому щоA)1 4 2 3 1 2 3 4 2 6 5 7 - всі числа різні

B)1 4 2 3 4 3 2 1 5 7 4 4 - є однакові числа

Знайти одну з «особливих» перестановок та кількість «особливих» перестановок.

Розв’язок. Ця задача має абсолютно такий саме розв’язок, як і задача 5.1.(про ферзів).

39

Page 40: Беркунський Збiрник Задач Пiдвищеноi Складностi

Заключення

В цьому посібнику було розглянуто деяку кількість задач, що в тому, чи іншому вигляді можуть складати основу для задач підвищеної складності, що пропонуються на олімпіадах з інформатики різних рівнів. Звичайно, як казав Козьма Прутков: «Нельзя объять необъятное», тому велика кількість задач залишилася поза межами цієї роботи. Виходячи з цього список літератури, що пропонується на закінчення можна розглядати не тільки як «список використаної літератури», а й як список літератури, що рекомендована для самостійного опанування.

40

Page 41: Беркунський Збiрник Задач Пiдвищеноi Складностi

Література

1. Вирт Н. Алгоритмы + структуры данных = программы. — М.: Мир, 1985.

2. Вирт Н. Систематическое программирование. Введение. — М.: Мир, 1977

3. Марченко А.И., Марченко Л.А. Программирование в среде Turbo Pascal 7.0 —М.: «Бином Универсал», К.: «ЮНИОР» 1997

4. Арсак Ж. Программирование игр и головоломок. — М.: «Наука». Гл. ред. физ.-мат. лит., 1990.

5. Дал У., Дейкстра Э., Хоор К. Структурное программирование. М.: Мир, 1975

6. Брудно А.Л., Каплан Л.И. Московские олимпиады по программированию. М.: Наука, 1990.

7. Бондарев В.М., Рублинецкий В.И., Качко Е.Г. Основы программирования — Харьков: Фолио, 1998.

41

Page 42: Беркунський Збiрник Задач Пiдвищеноi Складностi

ЗМІСТ

ПЕРЕДМОВА

РОЗДІЛ 1. ЗАДАЧІ, ЩО НЕ ВИКОРИСТОВУЮТЬ МАСИВІВ.

РОЗДІЛ 2. МАСИВИ

РОЗДІЛ 3. СОРТУВАННЯ МАСИВІВ

РОЗДІЛ 4. УТВОРЕННЯ КОМБІНАТОРНИХ ОБ’ЄКТІВ

РОЗДІЛ 5. ЗАДАЧІ ПЕРЕБОРУ ВАРІАНТІВ

ЗАКЛЮЧЕННЯ

ЛІТЕРАТУРА

42