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

Розставлення слонів на шахівниці

Знайдіть кількість способів розставити KK слонів на шахівниці розміру N×NN \times N так, щоб жодні два слони не били один одного.

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

Алгоритм

Цю задачу можна розв'язати за допомогою динамічного програмування.

Занумеруємо діагоналі шахівниці так: чорні діагоналі мають непарні індекси, білі — парні, а самі діагоналі нумеруються в незменшуваному порядку за кількістю клітинок у них. Ось приклад для шахівниці 5×55 \times 5.

12569 25698 56987 69874 98743 \begin{matrix} \bf{1} & 2 & \bf{5} & 6 & \bf{9} \\\ 2 & \bf{5} & 6 & \bf{9} & 8 \\\ \bf{5} & 6 & \bf{9} & 8 & \bf{7} \\\ 6 & \bf{9} & 8 & \bf{7} & 4 \\\ \bf{9} & 8 & \bf{7} & 4 & \bf{3} \\\ \end{matrix}

Нехай D[i][j] позначає кількість способів розставити j слонів на діагоналях з індексами до i, які мають той самий колір, що й діагональ i. Тоді i = 1...2N-1 і j = 0...K.

Ми можемо обчислити D[i][j], використовуючи лише значення D[i-2] (ми віднімаємо 2, бо розглядаємо тільки діагоналі того самого кольору, що й ii). Є два способи отримати D[i][j]. Або ми розставляємо всі j слонів на попередніх діагоналях: тоді є D[i-2][j] способів це зробити. Або ми ставимо одного слона на діагональ i, а j-1 слонів — на попередні діагоналі. Кількість способів зробити це дорівнює кількості клітинок у діагоналі i мінус j-1, бо кожен з j-1 слонів, розставлених на попередніх діагоналях, заблокує одну клітинку на поточній діагоналі. Кількість клітинок у діагоналі i можна обчислити так:

int squares (int i) {
if (i & 1)
return i / 4 * 2 + 1;
else
return (i - 1) / 4 * 2 + 2;
}

Базовий випадок простий: D[i][0] = 1, D[1][1] = 1.

Коли ми обчислили всі значення D[i][j], відповідь можна отримати так: розглянемо всі можливі кількості слонів, розставлених на чорних діагоналях i=0...K, з відповідними кількостями слонів на білих діагоналях K-i. Слони, розставлені на чорних і білих діагоналях, ніколи не б'ють один одного, тому розставлення можна робити незалежно. Індекс останньої чорної діагоналі — 2N-1, останньої білої — 2N-2. Для кожного i додаємо D[2N-1][i] * D[2N-2][K-i] до відповіді.

Реалізація

int bishop_placements(int N, int K)
{
if (K > 2 * N - 1)
return 0;

vector<vector<int>> D(N * 2, vector<int>(K + 1));
for (int i = 0; i < N * 2; ++i)
D[i][0] = 1;
D[1][1] = 1;
for (int i = 2; i < N * 2; ++i)
for (int j = 1; j <= K; ++j)
D[i][j] = D[i-2][j] + D[i-2][j-1] * (squares(i) - j + 1);

int ans = 0;
for (int i = 0; i <= K; ++i)
ans += D[N*2-1][i] * D[N*2-2][K-i];
return ans;
}