Всё сдал! - помощь студентам онлайн Всё сдал! - помощь студентам онлайн

Реальная база готовых
студенческих работ

Узнайте стоимость индивидуальной работы!

Вы нашли то, что искали?

Вы нашли то, что искали?

Да, спасибо!

0%

Нет, пока не нашел

0%

Узнайте стоимость индивидуальной работы

это быстро и бесплатно

Получите скидку

Оформите заказ сейчас и получите скидку 100 руб.!


Разработка алгоритма и программного обеспечения для решения прикладной задачи теории графов

Тип Реферат
Предмет Математика
Просмотров
1901
Размер файла
281 б
Поделиться

Ознакомительный фрагмент работы:

Разработка алгоритма и программного обеспечения для решения прикладной задачи теории графов

МИНИСТЕРСТВО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ

ГОСУДАРСТВЕННОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ

«ТЮМЕНСКИЙ ГОСУДАРСТВЕННЫЙ НЕФТЕГАЗОВЫЙ УНИВЕРСИТЕТ»

КАФЕДРА ИНФОРМАТИКИ И ВЫЧИСЛИТЕЛЬНОЙ ТЕХНИКИ

КУРСОВАЯ РАБОТА

По дисциплине «Дискретная математика»

Тема: Разработка алгоритма и программного обеспечения для решения прикладной задачи теории графов.

Выполнил

студент группы

АСОиУзс-07-01

Быстров Евгений М.

Шифр: 742301020016500

Проверил

Гапанович И.В..

Тюмень 2008


Содержание.

1. Введение. Постановка задачи.3
2. Назначение и область применения.3
3. Описание алгоритма решения задачи.3
4. Ручной просчёт.4
5. Описание программы.6
6. Тестирование программы.7
7. Литература.9
8. Приложение 1. Листинг программы.10

1. Введение. Постановка задачи.

Задание на курсовую работу по дисциплине «Дискретная математика».

Студент группы АСОиУзс-07-01 Быстров Евгений М.

Специальность «Автоматизированные системы обработки информации и управления»

Тема: Разработка алгоритма и программного обеспечения для решения прикладной задачи теории графов.

ЗАДАНИЕ 13. Построить гамильтонову цепь в графе, используя алгоритм с возвратом.

Определения.

Пусть G – псевдограф. Цепь в G называется гамильтоновой, если она проходит через каждую вершину псевдографа ровно один раз. Простейшим достаточным условием существования гамильтоновых цепей в графе является его полнота.

Граф G называется полным, если каждая его вершина смежна со всеми остальными вершинами. Необходимым условием существования гамильтоновых цепей являемся связность данного графа.

Граф G называется связным, если для любых двух его вершин существует маршрут, их соединяющий, то есть любая вершина достижима из любой вершины.

Матрица смежности графа – квадратная матрица, показывающая взаимосвязь вершин и рёбер.

2. Назначение и область применения.

Программа создана для решения прикладной задачи теории графов. Программа будет применяться для защиты курсовой работы.

3. Описание алгоритма решения задачи.

Наиболее простым методом выделения гамильтоновых цепей является метод перебора всевозможных перестановок всех вершин графа. Для каждой из них проверяется, является ли данный маршрут цепью, если нет, то переходим к другой перестановке. Тогда по окончании перебора будут выделены все гамильтоновы цепи в графе. При этом в случае полного графа ни одна из перестановок не окажется отброшенной, то есть данный метод является эффективным для графов, близких к полным.

Для того, что сократить число шагов в предложенном методе, рассмотрим следующий алгоритм. Предположим, что решение имеет вид последовательности v1…vi. Идея метода состоит в следующем: решение строится последовательно, начиная с пустой последовательности (длины 0). Далее, имея частичное решение ищем такое допустимое значение vi+1, которое ещё не было использовано, добавляем его к пройденному частичному решению и продолжаем процесс для нового частичного решения v1…vi+1. В противном случае, если такого значения vi+1 не существует, возвращаемся к предыдущему частичному решению v1…vi-1 и продолжаем процесс, пытаясь определить новое, еще не использованное допустимое значение vk. Такие алгоритмы носят название «алгоритмы с возвратом». Процесс поиска с возращением будет продемонстрирован в разделе «Ручной просчет» и «Тестирование программы» на примерах, показывающих как работает программа.

Алгоритм работы программы:

  1. Создаётся пустая последовательность цепи длиной v (количество вершин).
  2. Создаётся динамический массив для таблицы смежности графа, который заполняется из файла или прямо из программы.
  3. Начинается поиск цепи с первой вершины (нулевая для матрицы).
  4. Поочерёдно просматривается матрица смежности циклом for(где i - строка, j - столбец). Если от вершины идёт ребро, то проверяем была ли уже такая вершина. Если такая вершина уже есть в пути, то выходим из цикла и ищем следующую вершину. Если такой вершины не было, то записываем вершину в путь и продолжаем поиск с найденной вершины. Здесь же проверяем: если это последняя j-тая вершина, а ребер больше нет, то проверяем готова ли цепь, если последняя вершина пути не пуста, то цепь найдена. Если цепь не готова, записываем частичное решение сначала в переменную временной цепи (tempput), затем сравниваем это значение пути с уже имеющимися, если найдено совпадение, то возвращаемся на предыдущую вершину, а если такого пути не было, то записываем неполный путь в список временных путей.

Если идет не ребро, если это последняя j-тая вершина, а ребер больше нет, то проверяем готова ли цепь. Если последняя вершина пути не пуста, то цепь найдена. Если цепь не готова записываем частичное решение сначала в переменную временной цепи (tempput), затем сравниваем это значение пути с уже имеющимися, если найдено совпадение, то возвращаемся на предыдущую вершину, а если такого пути не было, то записываем неполный путь в список временных путей.

    Когда гамильтонова цепь найдена, она выводится в окно «Результат». Список частичных решений выводится в окно «Ход поиска цепи».

4. Ручной просчёт.

Процесс работы алгоритма с возвратом удобно показать на некотором дереве поиска, которое строится следующим образом. Каждая вершина дерева соответствует некоторому частичному решению. Корнем данного дерева является пустая последовательность. Для построения гамильтоновой цепи поиск будет начинаться с неё. При обходе в глубину посещаются все поддеревья, пока не будет найдена гамильтонова цепь.

Возьмём для примера следующий граф:

0, 1, 2, 3 – вершины графа;

Рёбра графа;

Гамильтонова цепь в графе.

Рис. 1 Граф.

Теперь покажем дерево данного графа:

0312 – гамильтонова цепь

Рис. 2 Дерево графа.

Составим матрицу смежности для данного графа:

v0123
00101
11011
20100
31100

0 – если между вершинами нет ребра

1 – если между вершинами есть ребро (или петля)

При расчёте гамильтоновой цепи, программа обращается к графу и работает с ним только посредством матрицы смежности.

Расчёт происходит так: сначала программа создает пустую цепь длиной 4 символа. Создаётся массив куда заносятся элементы матрицы. Берёт первый элементы пути и присваивает ему 0. То есть начинает поиск с нулевой вершины. Просматривает в матрице строку 0, находит значение 1 (есть ребро), проверяет номер столбца «1» и записывает номер столбца в путь, если такого там нет. Записывает неполный путь «01» в список временных путей. Далее поиск ведется в строке «1», находит 1 на пересечении строки «1» и столбца «0» и проверяет была ли такая вершина в пути, была, пропускает, ищет дальше, находит 1 на пересечении строки «1» и столбца «2». Проверка, такой вершины не было, записывает в маршрут (он теперь «012»), записывает неполный маршрут в список временных путей, ищет далее в строке «2». Смотрим: «2» «0» - значение 0 , «2» «1» - значение 1 было, «2» «2» - значение 0, «2» «3» - значение 0. Теперь получается что это последняя j-тая вершина (столбец), а ребер больше нет, программа проверяет готова ли цепь. Так как цепь не готова, записывает частичное решение в список временных путей. Делает шаг назад, запоминает последнюю вершину, затем стирает её, и начинает поиск с предыдущей, пропуская текущую. Теперь маршрут снова «01», программа ищет дальше в строке «1», пропуская столбец «2» и находит 1 в следующем столбце «3». Проверяет, такой не было, записывает в путь, записывает неполный путь в список путей. Ищет в строке «3», не находит ничего подходящего, делает шаг назад, и начинает снова поиск со строки «1» столбца «0». Доходит до столбца «2», тут проверка показывает, что в списке неполных путей такой путь уже был. Не записывая повтор, делает шаг назад в пути «01», стирает единицу, запомнив её, и продолжает поиск в строке «0», пропустив столбец «1». Находит 1 в столбце «3» (путь «03»), затем «031», и наконец завершает гамильтонову цепь, найдя «2». Цепь готова: «0312».


5. Описание программы.

Описание программы.

Программа состоит из одного главного окна (рис. 3)


Рис. 3 Главное окно программы.

Как видно из рисунка, программа состоит из трёх блоков:

  1. Входная информация. Здесь можно создать новый граф, указав количество вершин и его имя или загрузить готовые графы, которые записаны в папку с программой в текстовые файлы.
  2. Тоже входная информация, а также основной цикл поиска гамильтоновой цепи (кнопка «Построить гамильтонову цепь»). Если создаётся новый граф, то для него вручную вводятся значения наличия или отсутствия (1 или 0 соответственно) рёбер между вершинами. Если выбирается готовый, то эти значения подставляются автоматически.
  3. Выходная информация. Здесь выводится весь список временных вершин, а также результат: гамильтонова цепь. Также здесь можно сохранить набранный во втором блоке граф в текстовый файл.

Ограничения программы.

  1. Программа предусматривает действительное наличие гамильтоновой цепи в заданном графе. В случае, если цепи в графе не существует, то программа этого определить не сможет и либо произойдёт зависание программы из-за того что основной поисковый цикл войдёт в бесконечное цикл, либо программа выдаст ошибочный результат.
  2. В блоке 2 при ручном вводе значений, допускается ввод только 0 или 1. Другие значения, а также пустая ячейка матрицы смежности приведёт либо к ошибке программы, либо к её зависанию.

6. Тестирование программы.

Первый тест уже подробно продемонстрирован на рис. 1, 2, 3.

Далее будут продемонстрированы ещё два более сложных теста программы с рисунками графов и снимками внешнего вида программы после расчёта гамильтоновых цепей.

Рис. 4 Граф с 6-ю вершинами.

Рис. 5 Тест № 1. Расчет гамильтоновой цепи.



Рис. 6 Другой граф с 6-ю вершинами

Рис. 7 Тест № 2


Литература.

  1. В.С. Гапанович, И. В. Гапанович «Дискретная математика», уч. пособие для студентов ИВТ, ТГНГУ, Тюмень, 2002.
  2. http://programmersclub.ru/
  3. http://www.realcoding.net/article/rubric/CCplus

Приложение 1. Листинг программы.

//---------------------------------------------------------------------------

#include <vcl.h>

#pragma hdrstop

#include <fstream.h>

#include "Unit1.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)

#pragma resource "*.dfm"

TFA *FA;

int v, i, j, y;

AnsiString namegraf;

int **g = new int *[v]; //массив указателей на строки

//---------------------------------------------------------------------------

__fastcall TFA::TFA(TComponent* Owner)

: TForm(Owner)

{

}

//---------------------------------------------------------------------------

void __fastcall TFA::BnewgrafClick(TObject *Sender)

{

for (int s=0; s<StringGrid1->RowCount; s++)

{

StringGrid1->Rows[s]->Clear();

}

memo1->Lines->Clear();

Eput->Clear();

v=StrToInt(Ev->Text);

namegraf=Enamegraf->Text;

namegraf=namegraf+".txt";

StringGrid1->RowCount=v+1;

StringGrid1->ColCount=v+1;

for (int i=1; i<v+1; i++)

{

StringGrid1->Cells[0][i]=IntToStr(i-1);

StringGrid1->Cells[i][0]=IntToStr(i-1);

}

StringGrid1->Cells[0][0]="";

}

//---------------------------------------------------------------------------

void __fastcall TFA::FileListBox1Click(TObject *Sender)

{

for (int s=0; s<StringGrid1->RowCount; s++)

{

StringGrid1->Rows[s]->Clear();

}

memo1->Lines->Clear();

Eput->Clear();

ifstream f(FileListBox1->FileName.c_str());

f>>v;

StringGrid1->RowCount=v+1;

StringGrid1->ColCount=v+1;

for (int i=1; i<v+1; i++)

{

StringGrid1->Cells[0][i]=IntToStr(i-1);

StringGrid1->Cells[i][0]=IntToStr(i-1);

}

StringGrid1->Cells[0][0]="";

//создаём динамический массив для таблицы смежности графа

int **g = new int *[v]; //массив указателей на строки

for (int i=0; i<v; i++) //выделение памяти под каждую строку

g[i] = new int [v];

//заполняем его из файла

for (int i=0; i<v; i++)

for (int j=0; j<v; j++)

f>>g[i][j];

f.close(); //закрываем файл

//заполняем StringGrid1

for (int i=0; i<v; i++)

for (int j=0; j<v; j++)

StringGrid1->Cells[i+1][j+1] = g[i][j];

//создаём массив маршрута гамильтоновой цепи

int *p = new int [v];

for (int i=0; i<v; i++) //обнуляеммассивпути

{p[i]=-1;};

}

//---------------------------------------------------------------------------

void __fastcall TFA::BstartClick(TObject *Sender)

{

//создаём динамический массив для таблицы смежности графа

int **g = new int *[v]; //массив указателей на строки

for (int i=0; i<v; i++) //выделение памяти под каждую строку

g[i] = new int [v];

//заполняемегоиз SrtingGrid1

for (int i=0; i<v; i++)

for (int j=0; j<v; j++)

g[i][j]=StrToInt(StringGrid1->Cells[i+1][j+1]);

//создаём массив маршрута гамильтоновой цепи

int *p = new int [v];

for (int i=0; i<v; i++) //обнуляеммассивпути

{p[i]=-1;};

memo1->Lines->Append("0");

//поиск гамильтоновой цепи

AnsiString put;

int s=0,t=0; //счетчики

int temp=0;

AnsiString tempput; //временныйпуть

int y=0;

for (int k=0; k<v; k++) //для K-той вершины пути

{

if (p[0]==-1)

{

p[0]=0; //начнём поиск с первой вершины (нулевая для матрицы)

}

if (p[k]<0) //если К-тая вершина пути пуста, то ищем эту вершину

{

for (i=p[k-1]; i<v; i++) //присвоить i последнюювершинупути

for (j=temp; j<v; j++) //i - строка, j - столбец

switch (y=g[i][j])

{

case 1: //если от вершины идёт ребро, то

for (int x=0; x<v; x++) //проверяем была ли уже такая вершина

{

if (p[x]==j) //если да

{

s++; //фиксируем это счетчиком

}

}

if (s>0) //если счетчик изменится, значит

{ //такая вершина уже есть в пути

s=0;

break; //обнуляем счетчик и выходим из цикла

}

if (s==0) //если счетчик не изменился, то

{ //записываем вершину в путь

p[k]=j; //продолжаем поиск с найденной вершины

if (j+1==v) //если это последняя j-тая вершина,

{ //а ребер больше нет, то

if (p[v-1]!=-1) //проверяем готова ли цепь

{ //если последняя вершина пути не пуста

for (int x=0; x<v; x++)

{

put=put+IntToStr(p[x]);

}

Eput->Text=put;

return;

}

}

if (p[v-1]<0) //еслицепьнеготова

{ //записать ее в tempput

for (int x=0; x<v; x++)

{ //убрав -1

if (IntToStr(p[x])!=-1)

{

tempput=tempput+IntToStr(p[x]);

}

}

//сравнить это значение пути с уже имеющимися в memo1

for (int str=0; str<memo1->Lines->Count; str++)

{

if (StrToInt(tempput)==StrToInt(memo1->Lines->Strings[str]))

{ //если найдено совпадение

t++; //фиксируем это счетчиком

}

}

if (t==0) //если счетчик не изменится

{

memo1->Lines->Append(tempput); //записатьнеполныйпутьв memo1

temp=0;

}

else if (t>0) //если счетчик изменится, т.е. совпадение есть

{

t=0;

temp=p[k-1]; //вернуться на предыдущую вершину

if (temp<v-1)

{

temp++;

}

else if (temp==v-1) //контроль temp чтобы не вышел за границы

{ //возможного кол-ва вершин

temp=0;

}

if (temp>v-1)

{

temp=0;

p[0]=0;

}

for (int x=0; x<v; x++) //стираем все вершины до той,

{ //с которой нужно вести поиск

if (x>=k-1)

{

p[x]=-1;

}

}

k=k-2;

}

tempput="";

j=v-1;

i=v-1;

}

}

break;

case 0: //если идет не ребро

if (j+1==v) //если это последняя j-тая вершина,

{ //а ребер больше нет, то

if (p[v-1]!=-1) //проверяем готова ли цепь

{ //если последняя вершина пути не пуста

for (int x=0; x<v; x++)

{

put=put+IntToStr(p[x]);

}

Eput->Text=put;

return;

}

if (p[v-1]<0) //еслицепьнеготова

{ //записать ее в tempput

for (int x=0; x<v; x++)

{ //убрав -1

if (IntToStr(p[x])!=-1)

{

tempput=tempput+IntToStr(p[x]);

}

}

//сравнить это значение пути с уже имеющимися в memo1

for (int str=0; str<memo1->Lines->Count; str++)

{

if (StrToInt(tempput)==StrToInt(memo1->Lines->Strings[str]))

{ //если найдено совпадение

t++; //фиксируем это счетчиком

}

}

if (t==0) //если счетчик не изменится

{

memo1->Lines->Append(tempput); //записать неполный путь в memo1

}

else if (t>0) //если счетчик изменится, т.е. совпадение есть

{

t=0;

}

temp=p[k-1]; //вернуться на предыдущую вершину

if (temp<v-1)

{

temp++;

p[k-1]=-1;

}

else if (temp==v-1) //контроль temp чтобы не вышел за границы

{ //возможного кол-ва вершин

temp=0;

for (int x=0; x<v; x++) //стираем все вершины до той,

{ //с которой нужно вести поиск

if (x>=k-1)

{

p[x]=-1;

}

}

}

if (temp>v-1)

{

temp=0;

p[0]=0;

}

k=k-2;

tempput="";

j=v-1;

i=v-1;

}

}

break;

}

}

}

;

}

//---------------------------------------------------------------------------

void __fastcall TFA::BsavegrafClick(TObject *Sender)

{

//создаём динамический массив для таблицы смежности графа

int **g = new int *[v]; //массив указателей на строки

for (int i=0; i<v; i++) //выделение памяти под каждую строку

g[i] = new int [v];

//заполняемегоиз StringGrid1

for (int i=0; i<v; i++)

for (int j=0; j<v; j++)

g[i][j]=StrToInt(StringGrid1->Cells[i+1][j+1]);

ofstream f(namegraf.c_str());

f<<v<<endl;

//заполняем файл графом g

for (int i=0; i<v; i++)

for (int j=0; j<v; j++)

f<<g[i][j]<<" ";

f.close(); //закрываем файл

FileListBox1->Update();

}

//---------------------------------------------------------------------------

void __fastcall TFA::BdelgrafClick(TObject *Sender)

{

DeleteFileA(FileListBox1->FileName);

FileListBox1->Update();

}

//---------------------------------------------------------------------------

void __fastcall TFA::N3Click(TObject *Sender)

{

ShowMessage("Курсовая работа по дискретной математике. Выполнил студент гр. АСОиУзс-07-01 Быстров Евгений, вариант №13");

}

//---------------------------------------------------------------------------


Нет нужной работы в каталоге?

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

Цены ниже, чем в агентствах и у конкурентов

Вы работаете с экспертами напрямую. Поэтому стоимость работ приятно вас удивит

Бесплатные доработки и консультации

Исполнитель внесет нужные правки в работу по вашему требованию без доплат. Корректировки в максимально короткие сроки

Гарантируем возврат

Если работа вас не устроит – мы вернем 100% суммы заказа

Техподдержка 7 дней в неделю

Наши менеджеры всегда на связи и оперативно решат любую проблему

Строгий отбор экспертов

К работе допускаются только проверенные специалисты с высшим образованием. Проверяем диплом на оценки «хорошо» и «отлично»

1 000 +
Новых работ ежедневно
computer

Требуются доработки?
Они включены в стоимость работы

Работы выполняют эксперты в своём деле. Они ценят свою репутацию, поэтому результат выполненной работы гарантирован

avatar
Математика
История
Экономика
icon
159599
рейтинг
icon
3275
работ сдано
icon
1404
отзывов
avatar
Математика
Физика
История
icon
156450
рейтинг
icon
6068
работ сдано
icon
2737
отзывов
avatar
Химия
Экономика
Биология
icon
105734
рейтинг
icon
2110
работ сдано
icon
1318
отзывов
avatar
Высшая математика
Информатика
Геодезия
icon
62710
рейтинг
icon
1046
работ сдано
icon
598
отзывов
Отзывы студентов о нашей работе
63 457 оценок star star star star star
среднее 4.9 из 5
Тгу им. Г. Р. Державина
Реферат сделан досрочно, преподавателю понравилось, я тоже в восторге. Спасибо Татьяне за ...
star star star star star
РЭУ им.Плеханово
Альберт хороший исполнитель, сделал реферат очень быстро, вечером заказала, утром уже все ...
star star star star star
ФЭК
Маринаааа, спасибо вам огромное! Вы профессионал своего дела! Рекомендую всем ✌🏽😎
star star star star star

Последние размещённые задания

Ежедневно эксперты готовы работать над 1000 заданиями. Контролируйте процесс написания работы в режиме онлайн

Подогнать готовую курсовую под СТО

Курсовая, не знаю

Срок сдачи к 7 дек.

только что
только что

Выполнить задания

Другое, Товароведение

Срок сдачи к 6 дек.

1 минуту назад

Архитектура и организация конфигурации памяти вычислительной системы

Лабораторная, Архитектура средств вычислительной техники

Срок сдачи к 12 дек.

1 минуту назад

Организации профилактики травматизма в спортивных секциях в общеобразовательной школе

Курсовая, профилактики травматизма, медицина

Срок сдачи к 5 дек.

2 минуты назад

краткая характеристика сбербанка анализ тарифов РКО

Отчет по практике, дистанционное банковское обслуживание

Срок сдачи к 5 дек.

2 минуты назад

Исследование методов получения случайных чисел с заданным законом распределения

Лабораторная, Моделирование, математика

Срок сдачи к 10 дек.

4 минуты назад

Проектирование заготовок, получаемых литьем в песчано-глинистые формы

Лабораторная, основы технологии машиностроения

Срок сдачи к 14 дек.

4 минуты назад

2504

Презентация, ММУ одна

Срок сдачи к 7 дек.

6 минут назад

выполнить 3 задачи

Контрольная, Сопротивление материалов

Срок сдачи к 11 дек.

6 минут назад

Вам необходимо выбрать модель медиастратегии

Другое, Медиапланирование, реклама, маркетинг

Срок сдачи к 7 дек.

7 минут назад

Ответить на задания

Решение задач, Цифровизация процессов управления, информатика, программирование

Срок сдачи к 20 дек.

7 минут назад
8 минут назад

Все на фото

Курсовая, Землеустройство

Срок сдачи к 12 дек.

9 минут назад

Разработка веб-информационной системы для автоматизации складских операций компании Hoff

Диплом, Логистические системы, логистика, информатика, программирование, теория автоматического управления

Срок сдачи к 1 мар.

10 минут назад
11 минут назад

перевод текста, выполнение упражнений

Перевод с ин. языка, Немецкий язык

Срок сдачи к 7 дек.

11 минут назад
planes planes
Закажи индивидуальную работу за 1 минуту!

Размещенные на сайт контрольные, курсовые и иные категории работ (далее — Работы) и их содержимое предназначены исключительно для ознакомления, без целей коммерческого использования. Все права в отношении Работ и их содержимого принадлежат их законным правообладателям. Любое их использование возможно лишь с согласия законных правообладателей. Администрация сайта не несет ответственности за возможный вред и/или убытки, возникшие в связи с использованием Работ и их содержимого.

«Всё сдал!» — безопасный онлайн-сервис с проверенными экспертами

Используя «Свежую базу РГСР», вы принимаете пользовательское соглашение
и политику обработки персональных данных
Сайт работает по московскому времени:

Вход
Регистрация или
Не нашли, что искали?

Заполните форму и узнайте цену на индивидуальную работу!

Файлы (при наличии)

    это быстро и бесплатно