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.

209 lines
6.1 KiB
C

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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