|
|
/* Задача о ходе коня - правило Варнсдорфа */
|
|
|
|
|
|
#include <stdio.h>
|
|
|
#include <stdlib.h>
|
|
|
#include <limits.h>
|
|
|
|
|
|
/* Возможные смещения для хода коня */
|
|
|
const int moves[8][2] = {
|
|
|
{-2, -1}, {-2, 1}, /* 2 вниз, влево-вправо */
|
|
|
{2, -1}, {2, 1}, /* 2 вверх, влево-вправо */
|
|
|
{-1, -2}, {1, -2}, /* 2 влево, вверх-вниз */
|
|
|
{-1, 2}, {1, 2} /* 2 вправо, вверх-вниз */
|
|
|
};
|
|
|
|
|
|
int **create_array(int N);
|
|
|
void delete_array(int **array, int N);
|
|
|
void reset_array(int **array, int N);
|
|
|
/* Количество ходов из заданной клетки */
|
|
|
int sum_of_moves(int **array, int N, int x1, int y1);
|
|
|
/* x1 - строка, y1 - столбец */
|
|
|
void solve(int **array, int N, int x1, int y1, int *move_n);
|
|
|
void print_array(int **array, int N);
|
|
|
/* Проверка суммой */
|
|
|
int sum_check(int **array, int N);
|
|
|
int check(int **array, int N, int x, int y);
|
|
|
int is_looped(int **array, int N);
|
|
|
|
|
|
int main() {
|
|
|
/* Размер доски, доску считаем квадратной */
|
|
|
size_t N;
|
|
|
scanf("%zu", &N);
|
|
|
/* Массив, представляющий доску */
|
|
|
int **chessboard = NULL;
|
|
|
int move_n = 1;
|
|
|
int x1 = 1, y1 = 1;
|
|
|
|
|
|
chessboard = create_array(N);
|
|
|
|
|
|
/* Ставим коня */
|
|
|
chessboard[x1][y1] = 1;
|
|
|
solve(chessboard, N, x1, y1, &move_n);
|
|
|
|
|
|
int number_solution = check(chessboard, N, x1, y1);
|
|
|
|
|
|
if (number_solution)
|
|
|
print_array(chessboard, N);
|
|
|
else
|
|
|
printf("Нет решения.\n");
|
|
|
delete_array(chessboard, N);
|
|
|
}
|
|
|
|
|
|
void solve(int **array, int N, int x1, int y1, int *move_n) {
|
|
|
int min = INT_MAX;
|
|
|
/* Клетки для итераций */
|
|
|
int x2 = 0, y2 = 0;
|
|
|
/* Выбранная клетка */
|
|
|
int x3 = 0, y3 = 0;
|
|
|
/* Количество ходов из очередной клетки */
|
|
|
int sum = 0;
|
|
|
/* 0 - все соседние клетки заняты, либо тупик, либо конечный результат;
|
|
|
* 1 - продолжаем рекурсию со следующей клетки */
|
|
|
int cont = 0;
|
|
|
|
|
|
/* Перебираем возможные ходы */
|
|
|
for (int i = 0; i < 8; i++) {
|
|
|
x2 = x1 + moves[i][0];
|
|
|
y2 = y1 + moves[i][1];
|
|
|
/* Не ушли ли за доску */
|
|
|
if ((x2 >= 0 && y2 >= 0) && (x2 < N && y2 < N)) {
|
|
|
/* Не были ли уже в этой клетке */
|
|
|
if (array[x2][y2] == 0) {
|
|
|
/* По правилу Варнсдорфа выбираем поле, с котрого можно пойти на минимальное количество других */
|
|
|
sum = sum_of_moves(array, N, x2, y2);
|
|
|
if (sum < min) {
|
|
|
cont = 1;
|
|
|
min = sum;
|
|
|
x3 = x2; y3 = y2;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
if (cont) {
|
|
|
*move_n += 1;
|
|
|
array[x3][y3] = *move_n;
|
|
|
solve(array, N, x3, y3, move_n);
|
|
|
}
|
|
|
/* Когда не находится, куда еще идти, "выпадаем" из рекурсии. Либо мы нашли решение, либо попали в тупик (маловероятно, но возможно) */
|
|
|
}
|
|
|
|
|
|
int sum_of_moves(int **array, int N, int x1, int y1) {
|
|
|
int sum = 0;
|
|
|
int x2 = 0, y2 = 0;
|
|
|
/* Перебираем возможные ходы */
|
|
|
for (int i = 0; i < 8; i++) {
|
|
|
x2 = x1 + moves[i][0];
|
|
|
y2 = y1 + moves[i][1];
|
|
|
/* Не вылезли ли за доску */
|
|
|
if ((x2 >= 0 && y2 >= 0) && (x2 < N && y2 < N)) {
|
|
|
if (array[x2][y2] == 0) {
|
|
|
sum += 1;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return sum;
|
|
|
}
|
|
|
/* Сумма элементов массива должна быть равна сумме чисел от 1 до N (т.е. обошли всю доску) */
|
|
|
int sum_check(int **array, int N) {
|
|
|
long long unsigned sum = 0;
|
|
|
long long unsigned total_sum = 0;
|
|
|
if (N % 2 == 1)
|
|
|
sum = (1 + N * N) / 2 * ( N * N);
|
|
|
else
|
|
|
sum = (N * N) / 2 * (N * N + 1);
|
|
|
for (int i = 0; i < N; i++)
|
|
|
for (int j = 0; j < N; j++)
|
|
|
total_sum += array[i][j];
|
|
|
|
|
|
if (sum == total_sum) {
|
|
|
return 1;
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int check(int **array, int N, int x, int y) {
|
|
|
/* Решение верно */
|
|
|
if (sum_check(array, N)) {
|
|
|
return 1;
|
|
|
}
|
|
|
/* Если мы попали в тупик, то пробуем найти иной путь */
|
|
|
else {
|
|
|
int move_n = 1;
|
|
|
|
|
|
/* Начинаем перебирать все решения */
|
|
|
for (int x1 = 0; x1 < N; x1++) {
|
|
|
for (int y1 = 0; y1 < N; y1++) {
|
|
|
/* Ставим коня на пустую доску */
|
|
|
move_n = 1;
|
|
|
reset_array(array, N);
|
|
|
array[x1][y1] = 1;
|
|
|
solve(array, N, x1, y1, &move_n);
|
|
|
/* Если другое решение подходит, то выдаем его */
|
|
|
if (sum_check(array, N) && is_looped(array, N)) {
|
|
|
int difference = array[x][y] - 1;
|
|
|
/* Изменяем ходы, будто мы ходили с другого поля */
|
|
|
for (int x2 = 0; x2 < N; x2++) {
|
|
|
for (int y2 = 0; y2 < N; y2++) {
|
|
|
if (array[x2][y2] > difference) {
|
|
|
array[x2][y2] -= difference;
|
|
|
}
|
|
|
else {
|
|
|
array[x2][y2] = N * N + array[x2][y2] - difference;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return 2;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* Проверяем замкнутость маршрута */
|
|
|
int is_looped(int **array, int N) {
|
|
|
int start_x = 0, start_y = 0;
|
|
|
int end_x = 0, end_y = 0;
|
|
|
/* Ищем */
|
|
|
for (int x = 0; x < N; x++ )
|
|
|
for (int y = 0; y < N; y++) {
|
|
|
if (array[x][y] == 1) {
|
|
|
start_x = x;
|
|
|
start_y = y;
|
|
|
}
|
|
|
else if (array[x][y] == N*N) {
|
|
|
end_x = x;
|
|
|
end_y = y;
|
|
|
}
|
|
|
}
|
|
|
/* Если можем пойти конем из одной в другую, то маршрут замкнут */
|
|
|
if ((abs(end_x - start_x) == 1 && abs(end_y - start_y) == 2) || (abs(end_x - start_x) == 2 && abs(end_y - start_y) == 1))
|
|
|
return 1;
|
|
|
else
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
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 reset_array(int **array, int N) {
|
|
|
for (int i = 0; i < N; i++)
|
|
|
for (int j = 0; j < N; j++)
|
|
|
array[i][j] = 0;
|
|
|
}
|
|
|
void print_array(int **array, int N) {
|
|
|
for (int i = 0; i < N; i++) {
|
|
|
for (int j = 0; j < N; j++)
|
|
|
printf("%3d ", array[i][j]);
|
|
|
printf("\n");
|
|
|
}
|
|
|
}
|