Вы находитесь на странице: 1из 9

Технический Университет Молдовы

Департамент ISA

Отчет
по лабораторной работе № 3
по дискретной математике

Тема: поиск минимального и максимального пути на


графе при помощи алгоритмов Белмана-Калаба и
Форда.

Выполнил:
Проверила:

Кишинев 2017
Цель работы: Изучение алгоритмов поиска минимального пути в
графе. Разработка программы, реализующей данные алгоритмы
поиска минимального пути в взвешенном графе.

Алгоритм Форда

Алгоритм Форда позволяет от какой-либо исходной вершины Xi определить


минимальный путь до произвольной вершины Xj графа G.
Алгоритм Форда состоит из следующих шагов:
1) Стартовой вершине ставится число ноль, а для остальных вершин Xi графа
G ставятся в соответствие максимально возможные для данной задачи числа Hi;
2) На втором шаге вычисляются разности Hj-Hi для каждой вершины Xi, где Hj
- вес вершины, в которую ведет дуга (Xi,Xj). Здесь возможны три случая: Hj - Hi <
Lij, Hj-Hi=Lij, Hj-Hi>Lij, где Lij - вес дуги, ведущей из
вершины Xi в вершину Xj.
Случай, когда Hj-Hi>Lij позволяет нам уменьшить расстояние между
вершинами Xi и Xj благодаря следующим преобразованиям: Hj-Hi>Lij, Hj > Lij +
Hi, отсюда принимаем: Hj=Hi+Lij.
Второй шаг выполняется до тех пор, пока еще существуют пары (i,j), для
которых выполняется неравенство Hj-Hi>Lij.
По окончанию второго этапа метки вершин позволяют нам определить
значение минимального пути из исходной в конечную вершину;
3) На третьем этапе происходит определение минимального пути при
помощи обратного хода от конечной вершины к начальной, причем для вершины
Xj предшествующей будет вершина Xi, если для этой пары выполняется Hj-Hi=Lij.
Если существует несколько таких пар, то выбирается любая из них.

Алгоритм Белмана - Калаба


Использование алгоритма Беллмана-Калаба позволяет нам ускорить поиск
минимального пути между произвольной заданной парой вершин.
На первом этапе заданному графу ставится в соответствие взвешенная
матрица смежности M. Матрица формируется по следующим правилам:
1) M(i,j)=Lij, если существует дуга, ведущая из Xi в Xj, где Lij - вес этой дуги;
2) M(i,j)=max, где max - максимально возможное число для данной задачи,
если не существует дуги, ведущей из вершины Xi в Xj;
3) M(i,j)=0, если i=j.
На втором этапе строится вектор V0 по следующим правилам:
1) V0(i)=Lin, если существует дуга, ведущая из вершины Xi в вершину Xn, где
Xn - конечная заданная вершина, для которой ищется минимальный путь, Lin - вес
этой дуги;
2) V0(i)=max, где max - максимально возможное число для данной задачи,
если не существует дуги, ведущей из вершины Xi в Xn;
3) V0(i)=0, если i=j.
На k-той итерации строится вектор Vk по следующим правилам:
1) Vk(i)=min{Vk-1(j)+Lij}, где i=1..(n-1),j=1..n;
i<>j
2) Vk(n) = 0.
Итерации прекращаются, когда мы получаем вектор, равный предыдущему,
т.е.Vk=Vk-1.
После прекращения процесса элемент вектора Vk, имеющий наименьшее
значение, не равное нулю, дает нам длину минимального пути между заданной
парой вершин.

Код программы:
#define MAX 30000
using namespace std;
struct List{
int v;
int w;
struct List *next;
};
struct Graph{
int h;
int p;
struct List *first;
struct List *last;
}*G;
int maxFlag;
int N;
int V;
void Ford();
void BelmanKalab();
void PathFord(int );
void Menu();
void ListAdj();
void FreeList();
int main()
{
Menu();
return 0;
}
void BelmanKalab()
{
int i,j,k;
struct List *c;
int **M=(int **)malloc(N*sizeof(int *));
int *VK=(int *)malloc(N*sizeof(int ));
int *VK_1=(int *)malloc(N*sizeof(int ));
int *t,f=1;
int *P=(int *)malloc(N*sizeof(int ));
for(i=0;i<N;i++)
M[i]=(int *)malloc(N*sizeof(int));
for(i=0;i<N;i++)
for(j=0;j<N;j++)
M[i][j]=(i==j)?0:MAX;
for(i=0;i<N;i++){
c=G[i].first;
while(c!=G[i].last){
if(maxFlag){
M[i][c->v]= -(c->w);
} else{
M[i][c->v]=c->w;
}
c=c->next;
}
}
printf("Введите конечную вершину: ");
scanf("%d",&V);
V--;
for(i=0;i<N;i++){
VK_1[i]=M[i][V];
P[i]=-1;
}
while(f){
for(i=0;i<N;i++)
VK[i]=MAX;
for(i=0;i<N;i++)
for(j=0;j<N;j++)
if(i!=j && VK[i]>VK_1[j]+M[i][j]){
VK[i]=VK_1[j]+M[i][j];
P[i]=j;
}
VK[V]=0;
for(i=0;i<N && VK[i]==VK_1[i];i++);
f=(i==N)?0:1;
t=VK_1;
VK_1=VK;
VK=t;
}
for(i=0;i<N;i++){
printf("\nПуть от %d до %d равен ", i+1, V+1);
if(VK_1[i] > 25000)
printf("Не существует.");
else {
for(k=i,j=0;j<N && P[k]!=-1 && k!=V;j++){
printf("->%d",k+1);
k=P[k];
}
printf("->%d",V+1);
if(maxFlag){
printf(" с длиной равной %d.", -VK_1[i]);
} else {
printf(" с длиной равной %d.", VK_1[i]);
}
}
}
for(i=0;i<N;i++)
free(M[i]);
free(P);
free(M);
printf("\nV : ");
free(VK);
if(maxFlag) {
for(i = 0; i < N; i++){
if(VK_1[i] > 25000) {
printf(" -inf ");
} else {
printf(" %d ", -VK_1[i]);
}
}
} else {
for(i = 0; i < N; i++){
if(VK_1[i] == MAX) {
printf(" inf ");
} else {
printf(" %d ", VK_1[i]);
}
}
}
free(VK_1);
}
void Ford()
{
int *VK = (int *)malloc(N * sizeof(int));
int i,f=1;
for(i = 0; i < N; i++)
VK[i] = 50000;
struct List *c;
if(G==NULL)
return;
for(i=0;i<N;i++){
G[i].p=-1;
G[i].h=MAX;
}
printf("Введите стартовую вершину: ");
scanf("%d",&V);
G[--V].h=0;
while(f){
f=0;
for(i=0;i<N;i++){
c=G[i].first;
if(maxFlag){
while(c!=G[i].last){
if(G[c->v].h>G[i].h-c->w){
G[c->v].h=G[i].h-c->w;
G[c->v].p=i;
f=1;
}
c=c->next;
}
} else{
while(c!=G[i].last){
if(G[c->v].h>G[i].h+c->w){
G[c->v].h=G[i].h+c->w;
G[c->v].p=i;
f=1;
}
c=c->next;
}
}
}
}
for(i=0;i<N;i++){
printf("\nПуть от %d до %d равен ",V+1,i+1);
if(G[i].h==MAX)
printf("Не существует.");
else{
PathFord(i);
if(maxFlag){
printf(" с длиной равной %d.", -G[i].h);
} else {
printf(" с длиной равной %d.", G[i].h);
}
VK[i] = G[i].h;
}
}
printf("\nV : ");
if(maxFlag){
for(i = 0; i < N; i++){
if(VK[i] > 30000 || VK[i] < -30000)
printf(" -inf ");
else
printf(" %d ", -VK[i]);
}
} else {
for(i = 0; i < N; i++){
if(VK[i] > 30000 || VK[i] < -30000)
printf(" inf ");
else
printf(" %d ", VK[i]);
}
}
}
void PathFord(int v)
{
if(v!=V)
PathFord(G[v].p);
printf("->%d",v+1);
}
void Menu()
{
int i;
char ch;
printf("\n--------------------\n");
printf("1.Ввести граф\n");
printf("2.Минимальный путь по алгоритму Форда\n");
printf("3.Максимальный путь по алгоритму Форда\n");
printf("4.Минимальный путь по алгоритму Белмана-Калаба\n");
printf("5.Максимальный путь по алгоритму Белмана-Калаба\n");
printf("0.Выход\n");
do
scanf("%c", &ch);
while(ch<'0' || ch>'7');
printf("\n--------------------\n");
switch (ch)
{
case '1': ListAdj();break;
case '2': maxFlag = 0; Ford();scanf("%c", &ch);break;
case '3': maxFlag = 1; Ford();scanf("%c", &ch);break;
case '4': maxFlag = 0;BelmanKalab();scanf("%c", &ch);break;
case '5': maxFlag = 1;BelmanKalab();scanf("%c", &ch);break;
case '0': FreeList();return;
}
Menu();
}
void ListAdj()
{
int i,v,w;
struct List *c;
if(G)
FreeList();
printf("Введите колличество вершин: ");
scanf("%d",&N);
G=(Graph *)malloc(N*sizeof(Graph));
printf("\n");
for(i=0;i<N;i++){
printf("%d->",i+1);
G[i].first=(List *)malloc(sizeof(List));
G[i].last=G[i].first;
G[i].last->next=NULL;
G[i].last->v=-1;
scanf("%d",&v);
while(v){
scanf("%d",&w);
G[i].last->v=v-1;
G[i].last->w=w;
G[i].last->next=(List *)malloc(sizeof(List));
G[i].last=G[i].last->next;
G[i].last->next=NULL;
G[i].last->v=-1;
scanf("%d",&v);
}
}
}
void FreeList()
{
struct List *c,*t;
while(N--){
c=G[N].first;
while(c!=G[N].last){
t=c->next;
free(c);
c=t;
}
}
free(G);
}

Пример работы программы:

Введите количество вершин: 5

1->2 3
9 5
3 11
3 0
4 4->3
8 3
0 5
2->5 18
14 0
0 5->0
3->2

--------------------
1.Ввести граф
2.Минимальный путь по алгоритму Форда
3.Максимальный путь по алгоритму Форда
4.Минимальный путь по алгоритму Белмана-Калаба
5.Максимальный путь по алгоритму Белмана-Калаба
0.Выход
2
--------------------
Введите стартовую вершину: 1

Путь от 1 до 1 равен ->1 с длиной равной 0.


Путь от 1 до 2 равен ->1->3->2 с длиной равной 6.
Путь от 1 до 3 равен ->1->3 с длиной равной 3.
Путь от 1 до 4 равен ->1->4 с длиной равной 8.
Путь от 1 до 5 равен ->1->3->5 с длиной равной 14.
V : 0 6 3 8 14
--------------------
1.Ввести граф
2.Минимальный путь по алгоритму Форда
3.Максимальный путь по алгоритму Форда
4.Минимальный путь по алгоритму Белмана-Калаба
5.Максимальный путь по алгоритму Белмана-Калаба
0.Выход
5

--------------------
Введите конечную вершину: 5

Путь от 1 до 5 равен ->1->4->3->2->5 с длиной равной 28.


Путь от 2 до 5 равен ->2->5 с длиной равной 14.
Путь от 3 до 5 равен ->3->2->5 с длиной равной 17.
Путь от 4 до 5 равен ->4->3->2->5 с длиной равной 20.
Путь от 5 до 5 равен ->5 с длиной равной 0.
V : 28 14 17 20 0

Вывод: В этой работе были рассмотрены алгоритмы поиска


кратчайших путей между вершинами в графе, а именно алгоритм
Форда и алгоритм Беллмана-Калаба. Эти алгоритмы часто
используются для решения многих задач.