Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Зубенко, Омельчук - Програмування. Поглиблений курс

.pdf
Скачиваний:
58
Добавлен:
07.03.2016
Размер:
5 Мб
Скачать
☆

Розділ IV. АЛГОРИТМИ

Одночасно з розробкою діаграми класів розглянемо сценарії вико- ристання програми. Кожен сценарій реалізується сукупністю об'єктів (екземплярів класів), що взаємодіють між собою за допомогою відпо- відних методів. За різноманітними сценаріями взаємодії об'єктів кла- си наповнюються методами, що знаходить своє відображення на діа- грамі класів. Розглянуті сценарії взаємодії об'єктів відображаються в діаграмах послідовності а) й кооперації б):

: Актор

frm : Main

fnd : CFind

 

 

 

 

 

CFind()

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

setup( )

 

 

Ініціалізація

 

 

insertFrom(from

)

 

 

 

бази даних

 

 

 

 

 

 

 

 

 

 

рейсів

 

 

 

 

 

 

 

 

 

 

insertTo(to)

 

 

 

 

пошук

 

 

 

 

 

isFlight(from, to)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

route(to)

 

 

Показати

 

 

 

 

 

 

 

 

маршрут та

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

загальну відстань

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

а)

581

ПРОГРАМУВАННЯ

: Актор

fnd : CFind

3:insertFrom(from)

4:insertTo(to)

6:route(to)

frm : Main

б)

Стани, в яких може перебувати конкретний об'єкт, і процес зміни його станів у результаті деяких подій наведено в діаграмі станів:

БазуБазуданих рейсівів заповненоо

do/Додаванняя рейсрейсів

Рейсівдодано

do/do/Додаваннярейсу

ВибірВибірпунктувідправленняідпр ення

Пунктвідправлення Пункт вибрано

вибрано

ВибірВибірпунктупунктуприбуття

ПунктПункприбуттявибрано

entry/entry/Перевіркаревірка, що, щопунктприбуттянеєєпунктомввідправлення

Знаходженнямаршрутуу

Рейсівзнайдено

exit/exit/Друкмаршрутууту

582

Розділ IV. АЛГОРИТМИ

У нашій задачі необхідно проініціалізувати базу даних рейсів, ввести пункти виїзду та прибуття, знайти й надрукувати потрібний маршрут. Послідовність дій у програмі наведена на діаграмі діяльності за допомогою схем потоків керування:

Ініціалізація бази даних рейсів

Введення пунктів виїзду та прибуття

Ввести пункт виїзду

Ввести пункт прибуття

Знаходження потрібного маршруту

Чи знайдено

маршрут?

[ + ]

Виведення знайденого [ - ] маршруту

У процесі проектування програм визначають, які класи, атрибути, методи та їхні параметри будуть використані для розв'язання задачі. Діаграми показують послідовність взаємодії об'єктів. Тепер можна роз- починати написання тексту програми. Надалі побудовані діаграми пре- цедентів, станів і діяльності можуть бути застосовані для її тестування.

Застосуємо спочатку алгоритм пошуку в глибину для знаходження маршруту.

Приклад 4.36. Пошук у глибину.

Лістинг.

#include <iostream>

583

ПРОГРАМУВАННЯ

#include <vector> #include <stdio.h> #include <string.h>

class CStack { public:

CStack(char *from, char *to, int dist)

{

strcpy(m_sFrom, from); strcpy(m_sTo, to); m_iDist=dist;

};

char m_sFrom[20]; char m_sTo[20]; int m_iDist;

}; /*база даних рейсів*/

class CFl:public CStack { public:

CFl(char *from, char *to, int dist):CStack(from, to, dist){ m_cSkip=0;

};

char m_cSkip; /*використовується при пошуку з поверненням*/

};

class CFind { private:

vector<CFl*>flight;/*вектор рейсів*/

/*стек, що використовується для пошуку з поверненням*/

vector<CStack*>bt_stack; public:

/*методи, що використовуються при пошуку*/ void setup(void);

void route(char *to);

void assertFlight(char *from, char *to, int dist); void push(char *from, char *to, int dist);

void pop(char *from, char *to, int *dist); void isFlight(char *from, char *to);

int find(char *from, char *anywhere); int match(char *from, char *to); void setEmpty();

};

584

Розділ IV. АЛГОРИТМИ

Окремі записи вводяться в базу даних за допомогою методу assert_flight(), а всю інформацію про рейси ініціалізує метод setup(). Код потрібних методів:

/*Ініціалізація бази даних рейсів*/ void CFind::setup(void)

{

assertFlight("Kyev", "Lviv", 544); assertFlight("Lviv", " Tchernigiv", 701); assertFlight("Kyev", "Tchernigiv", 151); assertFlight("Kyev", "Vinnitsa", 266); assertFlight("Vinnitsa", "Donetsk", 812); assertFlight("Vinnitsa", "Simferopol", 801); assertFlight("Vinnitsa", "Lviv", 369); assertFlight("Tchernigiv", "Gitomir", 297); assertFlight("Tchernigiv", "Odessa", 634); assertFlight("Odessa", "Simferopol", 473); assertFlight("Donetsk", "Simferopol", 571);

}

/*Запам'ятати дані в базі*/

void CFind::assertFlight(char *from, char *to, int dist)

{

CFl *fl=new CFl(from, to, dist); flight.push_back(fl);

}

Для написання коду, що виконує пошук маршруту з Києва в Сім- ферополь, потрібні кілька допоміжних методів. По-перше, метод для визначення наявності рейсу між двома містами. Цей метод назвемо match(); він повертає відстань між двома містами, якщо такий рейс є, і нуль, якщо такого рейсу немає:

/*Якщо між двома містами є рейс, то повертається відстань між ними, інакше повертається 0.*/

int CFind::match(char *from, char *to)

{

for (vector<CFl*>::iterator t=flight.end()-1; t!=flight.begin(); t--)

if(!strcmp((*t)->m_sFrom, from) && !strcmp((*t)->m_sTo, to)) return (*t)->m_iDist;

return 0; /*не знайдено*/

}

Іншим необхідним методом є find(). Він шукає в базі даних будь- який рейс із зазначеного міста. Якщо рейс з яким-небудь іншим міс-

585

ПРОГРАМУВАННЯ

том знайдений, то повертаються назва цього міста й відстань до ньо- го від першого міста, інакше – нуль. Текст методу find():

/*Знаючи пункт відправлення (параметр from), знайти пункт прибуття (параметр anywhere)*/

int CFind::find(char *from, char *anywhere)

{

vector<CFl*>::iterator find_pos=flight.begin(); while(find_pos<flight.end()) { if(!strcmp((*find_pos)->m_sFrom, from) &&

!(*find_pos)->m_cSkip) { strcpy(anywhere, (*find_pos)->m_sTo); (*find_pos)->m_cSkip=1;//активізувати return (*find_pos)->m_iDist;

}

find_pos++;

}

return 0;

}

Якщо поле m_cSkip (пропустити) має значення 1, то рейс між міс- тами не обирається. Коли рейс знайдено, то його поле m_cSkip позна- чається як активне – таким чином реалізується пошук із поверненням з тупиків ■

Пошук із поверненням (бектрекінг) – важлива частина багатьох мето-

дів пошуку. У загальному вигляді він реалізується за допомогою рекур- сивних підпрограм і спеціального стеку. При проходженні графа всі ве- ршини, що зустрічаються, поміщаються у стек. При досягненні листка відбувається зупинка без можливості повторного спуску (у глибину), зі стеку забирається остання розташована там вершина й досліджується новий шлях, що починається з неї. Цей процес продовжується, поки не буде знайдена мета чи досліджені всі шляхи. Методи push() та pop() (означають відповідно "помістити у стек" і "виштовхнути зі стеку"), що керують стеком, використовуються для пошуку з поверненням. Значен- ня, що поміщаються у стек, зберігаються в контейнері bt_stack:

/*Підпрограми звернення до стеку*/

void CFind::push(char *from, char *to, int dist)

{

CStack *st=new CStack(from, to, dist); bt_stack.push_back(st);

}

void CFind::pop(char *from, char *to, int *dist)

{

586

Розділ IV. АЛГОРИТМИ

vector<CStack*>::iterator tos=bt_stack.end()-1; if(!bt_stack.empty()) {

strcpy(from, (*tos)->m_sFrom); strcpy(to, (*tos)->m_sTo); *dist=(*tos)->m_iDist; bt_stack.pop_back();

}

else cout<<"EMPTY Стек порожній."<<endl;

}

Метод isFlight() – головна підпрограма для пошуку маршруту:

//пошук у глибину

void CFind::isFlight(char *from, char *to)

{

int d, dist;

char anywhere[20];

//подивитися, чи є місто пунктом прибуття if(d=match(from, to)) {

push(from, to, d); return;

}

//перевірити інший рейс if(dist=find(from, anywhere)) {

push(from, to, dist); isFlight(anywhere, to);

}

else {

//пошук із поверненням pop(from, to, &dist); isFlight(from, to);

}

}

Метод match() перевіряє базу даних на наявність рейсу між міста- ми, на які вказують параметри m_sFrom та m_sTo. Якщо такий рейс є, то мета пошуку досягнута. Тоді дані про рейси поміщають у стек і метод повертає керування. Інакше метод find() перевіряє, чи є рейс між містом, на яке вказує from, і будь-яким іншим містом. Якщо є, то відповідні дані поміщаються у стек і рекурсивно викликається isFlight(). Інакше виконується пошук із поверненням. Попередня вершина видаляється зі стеку, і рекурсивно викликається isFlight(). Цей процес повторюється доти, доки не буде знайдена ціль. Поле m_cSkip використовується при пошуку з поверненням, щоб не переві- рялися повторно ті самі рейси:

587

ПРОГРАМУВАННЯ

/*Показати маршрут і загальну відстань*/ void CFind::route(char *to)

{

int dist;

vector<CStack*>::iterator t=bt_stack.begin(); dist=0;

while(t<bt_stack.end()) { cout<<(*t)->m_sFrom<<"to";

dist+=(*t)->m_iDist; t++;

}

cout<<to<<endl;

cout<<"Відстань у кілометрах:"<<dist<<endl;

}

/*Якщо між двома містами є рейс, то повертається відстань між ними, інакше повертається 0*/

int CFind::match(char *from, char *to)

{

for (vector<CFl*>::iterator t=flight.end()-1; t!=flight.begin(); t--)

if(!strcmp((*t)->m_sFrom, from) && !strcmp((*t)->m_sTo, to)) return (*t)->m_iDist;

return 0; /*не знайдено*/

}

Пошук у глибину маршруту від Києва до Сімферополя (повний текст програми):

Лістинг.

/*Пошук у глибину*/ #include <iostream> #include <vector> #include <stdio.h> #include <string.h>

using namespace std;

class CStack { public:

CStack(char *from, char *to, int dist)

{

strcpy(m_sFrom, from); strcpy(m_sTo, to); m_iDist=dist;

};

588

Розділ IV. АЛГОРИТМИ

char m_sFrom[20]; char m_sTo[20]; int m_iDist;

};

/*структура бази даних рейсів*/ class CFl:public CStack { public:

CFl(char *from, char *to, int dist):CStack(from, to, dist){ m_cSkip=0;

};

char m_cSkip; /*використовується при пошуку з поверненням*/

};

class CFind { private:

vector<CFl*>flight;/*вектор структур БД*/

/*стек, що використовується для пошуку з поверненням*/

vector<CStack*>bt_stack; public:

void setup(void); void route(char *to);

void assertFlight(char *from, char *to, int dist); void push(char *from, char *to, int dist);

void pop(char *from, char *to, int *dist); void isFlight(char *from, char *to);

int find(char *from, char *anywhere); int match(char *from, char *to); void setEmpty();

};

/*Ініціалізація бази даних рейсів*/ void CFind::setup(void)

{

assertFlight("Kyev", "Lviv", 544); assertFlight("Lviv", " Tchernigiv", 701); assertFlight("Kyev", "Tchernigiv", 151); assertFlight("Kyev", "Vinnitsa", 266); assertFlight("Vinnitsa", "Donetsk", 812); assertFlight("Vinnitsa", "Simferopol", 801); assertFlight("Vinnitsa", "Lviv", 369); assertFlight("Tchernigiv", "Gitomir", 297); assertFlight("Tchernigiv", "Odessa", 634);

589

ПРОГРАМУВАННЯ

assertFlight("Odessa", "Simferopol", 473); assertFlight("Donetsk", "Simferopol", 571);

}

/*Запам'ятати дані в базі*/

void CFind::assertFlight(char *from, char *to, int dist)

{

CFl *fl=new CFl(from, to, dist); flight.push_back(fl);

}

/*Показати маршрут і загальну відстань*/ void CFind::route(char *to)

{

int dist;

vector<CStack*>::iterator t=bt_stack.begin(); dist=0;

while(t<bt_stack.end()) { cout<<(*t)->m_sFrom<<"to";

dist+=(*t)->m_iDist; t++;

}

cout<<to<<endl;

cout<<"Відстань у кілометрах:"<<dist<<endl;

}

/*Якщо між двома містами є рейс, то повертається відстань між ними, інакше повертається 0*/

int CFind::match(char *from, char *to)

{

for (vector<CFl*>::iterator t=flight.end()-1; t!=flight.begin(); t--)

if(!strcmp((*t)->m_sFrom, from) && !strcmp((*t)->m_sTo, to)) return (*t)->m_iDist;

return 0; /*не знайдено*/

}

/*Знаючи пункт відправлення (параметр from), знайти пункт прибуття (параметр anywhere)*/

int CFind::find(char *from, char *anywhere)

{

vector<CFl*>::iterator find_pos=flight.begin(); while(find_pos<flight.end()) { if(!strcmp((*find_pos)->m_sFrom, from) &&

!(*find_pos)->m_cSkip) { strcpy(anywhere, (*find_pos)->m_sTo); (*find_pos)->m_cSkip=1;//активізувати

590