ФВТ (КХТП) / Материалы для КХТП - 2002 / 3_course / 1semester / ТОЛ (Мешалкин) / Ишова / Алгоритм Прима
.docКраткие теоретические основы
Кратчайший остов (SST) графа
Рассмотрим взвешенный связный неориентированный граф G = (X, А), вес ребра (xi, xj) обозначим cij. Из большого числа остовов графа нужно найти один, у которого сумма весов ребер наименьшая. Такая задача возникает, например, в том случае, когда вершины являются клеммами электрической сети, которые должны быть соединены друг с другом с помощью проводов наименьшей общей длины (для уменьшения уровня наводок).
Д
ругой
пример: вершины представляют города,
которые нужно связать сетью трубопроводов;
тогда наименьшая общая длина труб,
которая должна быть использована для
строительства (при условии, что вне
городов «разветвления» трубопроводов
не допускаются), определяется кратчайшим
остовом соответствующего графа.
Алгоритм Прима
Этот алгоритм порождает SST посредством разрастания только одного поддерева, например Ts, содержащего больше одной вершины. «Одиночные» вершины рассматриваются как отдельные поддеревья. Поддерево Ts постепенно разрастается за счет присоединения ребер (xi, xj), где xiTs и xjTs; причем добавляемое ребро должно иметь наименьший вес cij. Процесс продолжается до тех пор, пока число ребер в Ts не станет равным n-1. Тогда поддерево Ts будет требуемым SST.
Алгоритм начинает работу с присвоения каждой вершине xjTs, пометки [j, j], где j, на каждом шаге есть ближайшая к xj вершина из поддерева Ts, а j — вес ребра (j, xj). На каждом шаге выполнения алгоритма вершина, например xj*, с наименьшей пометкой j присоединяется к Ts посредством добавления ребра (j*, xj*). Поскольку к Ts добавлена новая вершина xj*, то, может быть, придется изменить пометки [j, j] у некоторых вершин xjTs, (если, например, с(xj, xj*) меньше существующей пометки j) и после этого продолжить процесс.
Описание алгоритма:
Шаг 1. Пусть Ts = {x1}, где x1 — первая вершина, и As = (As, является множеством ребер, входящих в SST).
Шаг 2. Для каждой вершины xjTs найти вершину jTs, такую, что
![]()
и приписать вершине xj пометку [j, j]. Если такой вершины j, нет, приписать вершине xj пометку [0, ].
Шаг 3. Выбрать такую вершину xj*, что
![]()
Обновить данные: Ts = Ts U {xj*}. As = As U {(j*, xj*)}. Если |Ts| = n, то остановиться. Ребра в As образуют SST. Если |Ts| n, то перейти к шагу 4.
Шаг 4. Для всех xjTs, таких, что xjГ(xj*), обновить пометки следующим образом:
Если j > с (xj*, xj), то положить j = с (xj*, xj), j = xj* и вернуться к шагу 3.
Если j с (xj*, xj), то перейти к шагу 3.
Блок-схема алгоритма

Инструкция по пользованию программой
Программа “Prim” используется для нахождения кратчайшего остова графа.
1. Начало работы с программой
Первое, что необходимо сделать – это задать количество вершин графа в соответствующем окне.
2. Заполнение матрицы
После нажатия кнопки “OK” на экране появится матрица нужного размера. Матрица является матрицей весов ребер графа. В ячейках матрицы задаются веса ребер, начало которых – номер строки, а конец – номер столбца. Если такого пути не существует, то в ячейке ставится «0». При задании мульти-графа веса параллельных ребер задаются через знак «-». Пример: 3-2-10.

Необходимо заполнить только ту половину матрицы, которая выше главной диагонали. Остальное сделает программа.
Внимание: матрица должна быть целиком заполнена!
4. Нахождение минимального остова графа
Для нахождения минимального остова графа надо нажать кнопку «Выполнить». После нажатия этой кнопки программа произведет необходимые вычисления в соответствии с прилагаемым алгоритмом и выведет результат в окно справа от матрицы. Результат выводится в виде:
Ts: As:
x1
x5 x1;3
x2 x5;3
x3 x2;3
x4 x5;3
,где Ts – вершина-источник, As – вершина-сток и вес ребра.
5. Дополнительные возможности
В меню «Файл» Вы можете распечатать результат, настроить принтер, сохранить результат в файл (в формате RichText) и выйти из программы. В меню «Редактирование» Вы можете изменить текущий шрифт.
Текст программы
unit prim1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Buttons, Grids, ComCtrls, Menus;
type
TForm1 = class(TForm)
Grid1: TStringGrid;
Edit1: TEdit;
Label1: TLabel;
BitBtn1: TBitBtn;
BitBtn2: TBitBtn;
Memo1: TMemo;
Label2: TLabel;
Label3: TLabel;
RichEdit1: TRichEdit;
MainMenu1: TMainMenu;
N1: TMenuItem;
N2: TMenuItem;
N3: TMenuItem;
N4: TMenuItem;
N5: TMenuItem;
PrintDialog1: TPrintDialog;
PrinterSetupDialog1: TPrinterSetupDialog;
FontDialog1: TFontDialog;
N6: TMenuItem;
N7: TMenuItem;
N8: TMenuItem;
SaveDialog1: TSaveDialog;
procedure BitBtn1Click(Sender: TObject);
procedure BitBtn2Click(Sender: TObject);
procedure Grid1SetEditText(Sender: TObject; ACol, ARow: Integer;
const Value: String);
procedure N5Click(Sender: TObject);
procedure N2Click(Sender: TObject);
procedure N3Click(Sender: TObject);
procedure N7Click(Sender: TObject);
procedure N8Click(Sender: TObject);
Function Multi(col,row:integer):integer;
private
{ Private declarations }
public
{ Public declarations }
end;
var
n:integer;
Form1: TForm1;
implementation
{$R *.DFM}
Function TForm1.Multi(col,row:integer):integer;
var str,temp:string;
ax:array of integer;
count,cx,bx,min:integer;
begin
str:=Grid1.Cells[col,row];
count:=0;
For cx:=1 to Length(str) do
begin
if str[cx]='-' then count:=count+1;
end;
if count=0 then SetLength(ax,1)
else SetLength(ax,count+1);
bx:=0;
For cx:=1 to Length(str) do
begin
if str[cx]<>'-' then temp:=temp+str[cx]
else
begin
ax[bx]:=StrToInt(temp);
bx:=bx+1;
temp:='';
end;
if cx=Length(str) then
begin
ax[bx]:=StrToInt(temp);
{ break;}
end;
end;
min:=10000;
For cx:=0 to count+1 do
begin
if ax[cx]<min then
begin
min:=ax[cx];
bx:=cx;
end;
end;
Multi:=min;
end;
{Процедура на нажатие кнопки "ОК"}
procedure TForm1.BitBtn1Click(Sender: TObject);
var ax,cx:integer;
begin
{Создание сетки нужного размера}
n:=StrToInt(Edit1.Text);
Grid1.ColCount:=n+1;
Grid1.RowCount:=n+1;
For ax:=1 to n do
Grid1.Cols[ax].Strings[0]:=IntToStr(ax);
For ax:=1 to n do
Grid1.Cols[0].Strings[ax]:=IntToStr(ax);
{Кнопку "Выполнить" делаем видимой}
BitBtn2.Enabled:=True;
end;
{Процедура на нажатие кнопки "Выполнить"}
procedure TForm1.BitBtn2Click(Sender: TObject);
var
cost:array of integer;
dest:array of integer;
ax,y,min,bx,z,cx:integer;
str:string;
{cost-массив весов; dest-массив вершин-стоков}
begin
For ax:=1 to n do
For cx:=1 to n do
if cx=ax then Grid1.Cells[ax,cx]:='0';
RichEdit1.Lines.Clear;
RichEdit1.Lines.Add('Исходный граф:');
RichEdit1.Lines.Add('');
For ax:=1 to n do
begin
str:=Grid1.Cells[ax,1];
For cx:=2 to n do
begin
str:=str+' ';
str:=str+Grid1.Cells[ax,cx]
end;
RichEdit1.Lines.Add(str);
end;
RichEdit1.Lines.Add('');
RichEdit1.Lines.Add('');
RichEdit1.Lines.Add('');
RichEdit1.Lines.Add('Минимальный остов графа:');
{Memo1 используется для вывода на экран результатов}
Memo1.Clear;
Memo1.Lines.Add('Ts: As:');
RichEdit1.Lines.Add('Ts: As:');
Memo1.Lines.Add('x1');
RichEdit1.Lines.Add('x1');
SetLength(cost,n+1);
SetLength(dest,n+1);
{Заносим в массивы начальные значения}
{------------- Шаг1 -------------}
For ax:=1 to n do
cost[ax]:=Multi(ax,1);
min:=100000;
For ax:=1 to n do
dest[ax]:=1;
{Нахождение ребра с минимальным весом}
{------------- Шаг2 -------------}
For ax:=2 to n do
if (cost[ax]>0) and (cost[ax]<min) then
begin
min:=cost[ax];
y:=ax;
end;
{Вывод на экран}
Memo1.Lines.Add('x'+IntToStr(y)+' x'+IntToStr(dest[y])+';'+IntToStr(cost[y]));
RichEdit1.Lines.Add('x'+IntToStr(y)+' x'+IntToStr(dest[y])+';'+IntToStr(cost[y]));
{Обновляем данные}
{------------- Шаг3 -------------}
For ax:=1 to n do
Grid1.Cells[1,ax]:=IntToStr(0);
For ax:=1 to n do
Grid1.Cells[y,ax]:=IntToStr(0);
cost[y]:=0;
bx:=2;
Repeat
{------------- Шаг4 -------------}
For ax:=2 to n do
if (Multi(ax,y)<>0) and ((cost[ax]=0) or (cost[ax]>Multi(ax,y))) then
begin
cost[ax]:=Multi(ax,y);
dest[ax]:=y;
end;
min:=100000;
{Нахождение ребра с минимальным весом}
For ax:=2 to n do
if (cost[ax]>0) and (cost[ax]<min) then
begin
min:=cost[ax];
z:=ax;
end;
{Вывод на экран}
Memo1.Lines.Add('x'+IntToStr(z)+' x'+IntToStr(dest[z])+';'+IntToStr(cost[z]));
RichEdit1.Lines.Add('x'+IntToStr(z)+' x'+IntToStr(dest[z])+';'+IntToStr(cost[z]));
{Обновляем данные}
For ax:=1 to n do
Grid1.Cells[z,ax]:=IntToStr(0);
cost[z]:=0;
y:=z;
bx:=bx+1;
Until bx=n;
end;
procedure TForm1.Grid1SetEditText(Sender: TObject; ACol, ARow: Integer;
const Value: String);
begin
Grid1.Cells[ARow,Acol]:=Grid1.Cells[Acol,Arow];
end;
procedure TForm1.N5Click(Sender: TObject);
begin
Application.Terminate;
end;
procedure TForm1.N2Click(Sender: TObject);
begin
if PrintDialog1.Execute then
RichEdit1.Print('Алгоритм Прима');
end;
procedure TForm1.N3Click(Sender: TObject);
begin
PrinterSetupDialog1.Execute;
end;
procedure TForm1.N7Click(Sender: TObject);
begin
FontDialog1.Font:=Memo1.Font;
if FontDialog1.Execute then begin
Memo1.Font:=FontDialog1.Font;
RichEdit1.Font:=FontDialog1.Font;
end;
end;
procedure TForm1.N8Click(Sender: TObject);
begin
if SaveDialog1.Execute then
RichEdit1.Lines.SaveToFile(SaveDialog1.FileName);
end;
end.
Список литературы
Н.Кристофидос «Теория графов. Алгоритмический подход.»
