Добавил:
baraevaliza
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Лабы по ПиАА 2024 / Baraeva_Elizaveta_lb1
.pdf
МИНОБРНАУКИ РОССИИ
САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ
ЭЛЕКТРОТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ
«ЛЭТИ» ИМ. В. И. УЛЬЯНОВА (ЛЕНИНА)
Кафедра МО ЭВМ
ОТЧЕТ
по лабораторной работе №1
по дисциплине «Построение и анализ алгоритмов»
Тема: Поиск с возвратом
Студентка гр. 2383
Бараева Е. Н.
Преподаватель
Шевелева А. М.
Санкт-Петербург
2024

2
Цель работы.
Изучить информацию об алгоритме поиска с возвратом. Решить
поставленную задачу, используя алгоритм поиска с возвратом, на языке C++.
Задание.
У Вовы много квадратных обрезков доски. Их стороны (размер)
изменяются от 1 до N−1, и у него есть неограниченное число обрезков
любого размера. Но ему очень хочется получить большую столешницу квадрат размера N. Он может получить ее, собрав из уже имеющихся
обрезков(квадратов).
Например, столешница размера 7×7 может быть построена из 9
обрезков.
Рисунок 1 – Пример столешницы
Внутри столешницы не должно быть пустот, обрезки не должны
выходить за пределы столешницы и не должны перекрываться. Кроме того,
Вова хочет использовать минимально возможное число обрезков.

3
Входные данные
Размер столешницы - одно целое число N (2≤N≤20).
Выходные данные
Одно число K, задающее минимальное количество обрезков(квадратов),
из которых можно построить столешницу(квадрат) заданного размера N.
Далее должны идти K строк, каждая из которых должна содержать три целых
числа, x,y и w, задающие координаты левого верхнего угла (1≤x,y≤N) и длину
стороны соответствующего обрезка(квадрата).
Вариант 1.
Выполнить исследование задачи на изменение времени поиска решения
в зависимости от размера квадрата.
Основные теоретические положения.
Данную задачу можно разделить на 2 случая: длина столешницы –
составное или простое число. Для составных четных чисел количество досок
будет 4, а их длина N / 2. Для составных нечетных числе задача сводится к
нахождению минимального простого делителя и поиску для него решения, а
затем домножения этого решения на N / min_div. А для простых чисел
разумнее всего расположить 3 квадрата размеров (N + 1) / 2, (N - 1) / 2, (N - 1)
/ 2 в три угла так, чтобы два маленьких квадрата соприкасались сторонами с
большим. Затем для оставшейся фигуры искать решение.
Выполнение работы.
• void data_optimization() – функция, которая преобразовывает данные:
ищет коэффициент домножения ответа, уменьшает размер поля, рисует
первые 3 квадрата на карте и добавляет их координаты и размер в
массив для ответа.
• int min_div(int x) – функция поиска минимального простого делителя.
• void set_square(int x, int y, int w, int setter) – функция рисовки или
стирания квадрата на карте.

4
• int check_square(int x, int y, int w) – функция проверки места для
расположения квадрата.
• void backtracking(int coord) – рекурсивная функция, которая ищет
лучшее решение расположения квадратов в оставшемся месте. В начале
двигается координата до ближайшего пустого места. Затем проверяется,
не дошли ли до конца, если да, то осуществляется проверка пустоты и
оптимальности ответа, перезапись его (если нужно) и возврат из
функции. После функция высчитывает координаты, размер
помещающегося квадрата, рисует его, записывая координаты в ответ, и
вызывает себя. В конце квадрат стирается и координаты его тоже.
• int main() – в функции считывается размер столешницы, карта
заполняется нулями, вызывается data_optimization, backtracking и void
output().
• void output() – функция выводит ответ на экран.
Разработанный программный код см. в приложении А.
Тестирование.
Результаты тестирования представлены в табл. 1.
Таблица 1 – Результаты тестирования
№ п/п
Входные данные
Выходные данные
Комментарии
1.
7
9
1 1 4
1 5 3
5 1 3
4 5 2
4 7 1
5 4 1
5 7 1
6 4 2
6 6 2
Простое число.
Верно.

5
2.
9
6
1 1 6
1 7 3
7 1 3
4 7 3
7 4 3
7 7 3
Составное
нечетное число.
Верно.
3.
16
4
1 1 8
1 9 8
9 1 8
9 9 8
Составное четное
число. Верно.
4.
23
13
1 1 12
1 13 11
13 1 11
12 13 2
12 15 5
12 20 4
13 12 1
14 12 3
16 20 1
16 21 3
17 12 7
17 19 2
19 19 5
Простое число.
Верно.
Исследование.
Результаты исследования работы алгоритма представлены в табл. 2.
Таблица 2 – Результаты исследования
N
sec
2
0,0000084
3
0,0000101
4
0,0000099
5
0,0000134
6
0,0000112
7
0,0000184
8
0,0000118
9
0,0000137
10
0,0000130
11
0,0000726
12
0,0000130
13
0,0001574
14
0,0000141
15
0,0000174
16
0,0000168
17
0,0009365
18
0,0000224
19
0,0025387
20
0,0000235
21
0,0000246
22
0,0000232
23
0,0095506
24
0,0000236
25
0,0000272
26
0,0000243
27
0,0000265
28
0,0000256
29
0,0670108
30
0,0000256
31
0,1571048
32
0,0000267
33
0,0000296
34
0,0000292
35
0,0000319
36
0,0000310
37
0,8320149
38
0,0000321
39
0,0000383
40
0,0000377

6
График отношения размера столешницы ко времени работы алгоритма
см. рис. 2.
Рисунок 2 – График исследования
По графику можно сделать вывод о том, что значение в составных
числах максимально приближены к нулю, а также о том, что наибольшее
значение времени достигается на больших простых числах (23, 29, 31, 37).
Это связно с тем, что не получается оптимизировать данные с заменой числа
на его наименьший простой делитель.
Вывод.
Был изучен алгоритм поиска с возвратом, а также написан код для
решения практической задачи.
Для оптимизации алгоритма было вычислено разбиения только для
простых чисел, а для составных чисел ответ домножался на коэффициент.
Также перед алгоритмом всегда расстанавливались три квадрат размерами:
(N + 1) / 2, (N - 1) / 2, (N - 1).

7
Еще было проведено исследование роста времени выполнения
алгоритма при увеличении размера квадрата. Результаты показали, что
худший случай алгоритма – большое простое число. Лучший случай –
составное число, которое можно сократить до наименьшего простого, что в
разы уменьшает время работы программы. Сложность алгоритма по времени
является экспоненциальной.

8
ПРИЛОЖЕНИЕ А
ИСХОДНЫЙ КОД ПРОГРАММЫ
Название файла: main.cpp
#include <iostream>
#include <vector>
#include <array>
#include <cmath>
int N, n, k = 0, cell_count = 0;
std::vector<std::array<int,3>> answer, curr_answer;
std::vector<std::vector<int>> map;
int min_div(int x) {
if (x % 2 == 0)
return 2;
for (int i = 3; i < int(sqrt(x)) + 1; i += 2)
if (x % i == 0)
return i;
return x;
}
void set_square(int x, int y, int w, int setter) {
for (int i = x; i < x + w; i++)
for (int j = y; j < y + w; j++)
map[i][j] = setter;
}
int check_square(int x, int y, int w) {
if (y + w > n or x + w > n)
return 0;
for (int i = x; i < x + w; i++)
for (int j = y; j < y + w; j++)
if (map[i][j])
return 0;
return 1;
}
void backtracking(int coord) {
if (coord < cell_count)
while (map[coord / n][coord % n]) {
coord++;
if (coord == cell_count)
break;
}
if (coord == cell_count) {
if (answer.empty() or curr_answer.size() < answer.size())
answer = curr_answer;
return;
}

9
if (answer.empty() or curr_answer.size() + 1 < answer.size())
{
int x = coord / n;
int y = coord % n;
int max_square = std::min(n - x, n - y);
while (not check_square(x, y, max_square))
max_square--;
for (int size_square = max_square; size_square > 0;
size_square--) {
set_square(x, y, size_square, 1);
curr_answer.push_back({x, y, size_square});
backtracking(coord + size_square);
set_square(x, y, size_square, 0);
curr_answer.pop_back();
}
}
}
void data_optimization() {
k = n / min_div(n);
n /= k;
cell_count = n * n;
set_square(0, 0, (n + 1) / 2, 1);
set_square(0, (n + 1) / 2, n / 2, 1);
set_square((n + 1) / 2, 0, n / 2, 1);
curr_answer.push_back({0, 0, (n + 1) / 2});
curr_answer.push_back({0, (n + 1) / 2, n / 2});
curr_answer.push_back({(n + 1) / 2, 0, n / 2});
}
void output() {
std::cout << answer.size() << std::endl;
for (int i = 0; i < answer.size(); i++)
std::cout << answer[i][0] * k + 1 << " " << answer[i][1]
* k + 1 << " " << answer[i][2] * k << std::endl;
}
int main() {
std::cin >> N;
n = N;
for (int i = 0; i < N; i++)
map.emplace_back(N, 0);
data_optimization();
backtracking(0);
output();
return 0;
}
Соседние файлы в папке Лабы по ПиАА 2024
