Archived
1
0
Fork 0
This repository has been archived on 2022-06-20. You can view files and clone it, but cannot push or open issues or pull requests.
mipt_clang/algo/second/knight.c
2022-06-20 07:16:15 +03:00

205 lines
6.1 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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