nihonium
/
mipt_clang
Archived
1
0
Fork 0
You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

206 lines
6.1 KiB
C

/* Задача о ходе коня - правило Варнсдорфа */
#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*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");
}
}