|
|
/* Задача о ферзях */
|
|
|
#include <stdio.h>
|
|
|
#include <stdlib.h>
|
|
|
#define _DEBUG
|
|
|
|
|
|
/* Глобальная переменная для количества решений, удобнее, чем таскать указатель на счетчик по рекурсии */
|
|
|
int SOLUTIONS = 0;
|
|
|
|
|
|
int **create_array(int N);
|
|
|
void delete_array(int **array, int N);
|
|
|
void print_array(int **array, int N);
|
|
|
void solve(int **array, int *lines, int N);
|
|
|
int **copy_array_2d(int **array, int N);
|
|
|
int *copy_array_1d(int *array, int N);
|
|
|
int first_empty_line(int *arrray, int N);
|
|
|
int array_is_any_empty(int *array, int N);
|
|
|
int count_busy_lines(int *array, int N);
|
|
|
void put_the_queen(int **chessboard, int *lines, int N, int column, int line);
|
|
|
|
|
|
int main() {
|
|
|
int N;
|
|
|
/* Во время отладки явно задаем размер */
|
|
|
#ifdef _DEBUG
|
|
|
N = 4;
|
|
|
#else
|
|
|
scanf("%d", &N);
|
|
|
#endif
|
|
|
int **chessboard = NULL;
|
|
|
/*
|
|
|
* lines содержит информацию о расстановке ферзей по строкам (занято-не занято)
|
|
|
* lines[0] - 1 строка и т.д.
|
|
|
*/
|
|
|
int *lines = NULL;
|
|
|
chessboard = create_array(N);
|
|
|
lines = (int*)calloc(N, sizeof(int));
|
|
|
|
|
|
solve(chessboard, lines, N);
|
|
|
printf("%d", SOLUTIONS);
|
|
|
|
|
|
delete_array(chessboard, N);
|
|
|
free(lines);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* 0 - пустая клетка;
|
|
|
* 1 - клетка, которую бьёт ферзь;
|
|
|
* 2 - клетка, в которой стоит ферзь;
|
|
|
* Если данный столбец (строка находится из lines) не занят, ставим туда ферзя и красим фрагменты доски, которые бьет новый ферзь в 1
|
|
|
* Если столбец занят, то пропускаем
|
|
|
*/
|
|
|
void solve(int **chessboard, int *lines, int N) {
|
|
|
/* Если больше нет пустых строк(без ферзей), мы заполнили доску ферзями */
|
|
|
if (!array_is_any_empty(lines, N)) {
|
|
|
print_array(chessboard, N);
|
|
|
SOLUTIONS++;
|
|
|
}
|
|
|
else {
|
|
|
/* Создаем копию текущего состояния */
|
|
|
int **chessboard1 = copy_array_2d(chessboard, N);
|
|
|
int *lines1 = copy_array_1d(lines, N);
|
|
|
/* Ищем первую строку без ферзя на ней */
|
|
|
int line = first_empty_line(lines1, N);
|
|
|
/* Будем пробовать ставить ферзя на разные места в строке */
|
|
|
for (int column = 0; column < N; column++) {
|
|
|
put_the_queen(chessboard1, lines1, N, column, line);
|
|
|
/* Если смогли поставить ферзя (т.е. кол-во пустых строк стало не равно предыдущему значению), то рекурсивно рассматриваем дальше */
|
|
|
if (!(count_busy_lines(lines1, N) == count_busy_lines(lines, N))) {
|
|
|
solve(chessboard1, lines1, N);
|
|
|
delete_array(chessboard1, N);
|
|
|
/* Возвращем массив в состояние до входа в рекурсию, соответствующего ферзя убираем */
|
|
|
chessboard1 = copy_array_2d(chessboard, N);
|
|
|
lines1[line] = 0;
|
|
|
}
|
|
|
}
|
|
|
free(lines1);
|
|
|
delete_array(chessboard1, N);
|
|
|
}
|
|
|
}
|
|
|
void put_the_queen(int **chessboard, int *lines, int N, int column, int line) {
|
|
|
/* Если клетка занята/под боем - пропускаем */
|
|
|
if (chessboard[line][column]) {
|
|
|
return;
|
|
|
}
|
|
|
/* "Занимаем" строку ферзем */
|
|
|
lines[line] = 1;
|
|
|
|
|
|
/* Проверяем, в какие клетки доски попадет ферзь и красим их в 1 */
|
|
|
for (int x = 0; x < N; x++) {
|
|
|
for (int y = 0; y < N; y++) {
|
|
|
if (abs(x - column) == abs(y - line) || x == column || y == line) {
|
|
|
chessboard[y][x] = 1;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
/* Ставим ферзя */
|
|
|
chessboard[line][column] = 2;
|
|
|
}
|
|
|
|
|
|
int **create_array(int N) {
|
|
|
int **array = (int**)calloc(N, sizeof(int*));
|
|
|
for (int i = 0; i < N; i++) {
|
|
|
array[i] = (int*)calloc(N, sizeof(int));
|
|
|
}
|
|
|
return array;
|
|
|
}
|
|
|
void delete_array(int **array, int N) {
|
|
|
for (int i = 0; i < N; i++) {
|
|
|
free(array[i]);
|
|
|
}
|
|
|
free(array);
|
|
|
}
|
|
|
void print_array(int **array, int N) {
|
|
|
printf("............\n");
|
|
|
for (int i = 0; i < N; i++) {
|
|
|
for (int j = 0; j < N; j++) {
|
|
|
printf("%c ", (array[i][j] == 2) ? ('Q') : ('*'));
|
|
|
}
|
|
|
printf("\n");
|
|
|
}
|
|
|
printf("............\n");
|
|
|
}
|
|
|
int **copy_array_2d(int **array, int N) {
|
|
|
int **array1 = create_array(N);
|
|
|
for (int i = 0; i < N; i++) {
|
|
|
for (int j = 0; j < N; j++) {
|
|
|
array1[i][j] = array[i][j];
|
|
|
}
|
|
|
}
|
|
|
return array1;
|
|
|
}
|
|
|
int *copy_array_1d(int *array, int N) {
|
|
|
int *array1 = (int*)calloc(N, sizeof(int));
|
|
|
for (int i = 0; i < N; i++) {
|
|
|
array1[i] = array[i];
|
|
|
}
|
|
|
return array1;
|
|
|
}
|
|
|
int first_empty_line(int *array, int N) {
|
|
|
for (int i = 0; i < N; i++) {
|
|
|
if (array[i] == 0) {
|
|
|
return i;
|
|
|
}
|
|
|
}
|
|
|
return -1;
|
|
|
}
|
|
|
int array_is_any_empty(int *array, int N) {
|
|
|
for (int i = 0; i < N; i++) {
|
|
|
if (array[i] == 0) {
|
|
|
return 1;
|
|
|
}
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
int count_busy_lines(int *array, int N) {
|
|
|
int sum = 0;
|
|
|
for (int i = 0; i < N; i++) {
|
|
|
if (array[i] == 1) {
|
|
|
sum++;
|
|
|
}
|
|
|
}
|
|
|
return sum;
|
|
|
}
|