Перейти до основного вмісту

Гра в п'ятнашки (15-puzzle): існування розв'язку

Ця гра відбувається на дошці розміром 4×44 \times 4. На дошці розташовано 1515 фішок, пронумерованих від 1 до 15. Одна клітинка лишається порожньою (позначається нулем 0). Потрібно привести дошку до показаного нижче положення, послідовно переміщуючи одну з фішок на вільне місце:

1234567891011121314150\begin{matrix} 1 & 2 & 3 & 4 \\ 5 & 6 & 7 & 8 \\ 9 & 10 & 11 & 12 \\ 13 & 14 & 15 & 0 \end{matrix}

Гру «15 Puzzle» створив Noyes Chapman у 1880 році.

Коли підходить цей алгоритм?
  • Чи потрібно лише перевірити існування розв'язку, а не знайти послідовність ходів?
  • Чи зводиться допустимий хід до переміщення порожньої клітинки між сусідами (тобто чи інваріант — парність перестановки + рядок порожньої клітинки)?
  • Чи можна закодувати позицію як перестановку й порахувати кількість інверсій?

Існування розв'язку

Розглянемо таку задачу: задано положення на дошці, потрібно визначити, чи існує послідовність ходів, яка приводить до розв'язку.

Припустимо, що ми маємо деяке положення на дошці:

a1a2a3a4a5a6a7a8a9a10a11a12a13a14a15a16\begin{matrix} a_1 & a_2 & a_3 & a_4 \\ a_5 & a_6 & a_7 & a_8 \\ a_9 & a_{10} & a_{11} & a_{12} \\ a_{13} & a_{14} & a_{15} & a_{16} \end{matrix}

де один з елементів дорівнює нулю та позначає порожню клітинку az=0a_z = 0

Розглянемо перестановку:

a1a2...az1az+1...a15a16a_1 a_2 ... a_{z-1} a_{z+1} ... a_{15} a_{16}

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

Нехай NN — кількість інверсій у цій перестановці (тобто кількість таких елементів aia_i та aja_j, що i<ji < j, але ai>aja_i > a_j).

Нехай KK — індекс рядка, у якому розташований порожній елемент (тобто, за нашою домовленістю, K=(z1)÷ 4+1K = (z - 1) \div \ 4 + 1).

Тоді розв'язок існує тоді й лише тоді, коли N+KN + K парне.

Реалізація

Описаний вище алгоритм можна проілюструвати таким програмним кодом:

int a[16];
for (int i=0; i<16; ++i)
cin >> a[i];

int inv = 0;
for (int i=0; i<16; ++i)
if (a[i])
for (int j=0; j<i; ++j)
if (a[j] > a[i])
++inv;
for (int i=0; i<16; ++i)
if (a[i] == 0)
inv += 1 + i / 4;

puts ((inv & 1) ? "No Solution" : "Solution Exists");

Доведення

У 1879 році Johnson довів, що якщо N+KN + K непарне, то розв'язку не існує, а того ж року Story довів, що всі положення, для яких N+KN + K парне, мають розв'язок.

Однак усі ці доведення були доволі складними.

У 1999 році Archer запропонував значно простіше доведення (його статтю можна завантажити тут).

Задачі для практики

Відеоматеріали