на тему «Задача коммивояжера

advertisement
Министерство образования и науки РФ
ФГБОУ ВПО «Восточно-Сибирский государственный университет технологий и управления»
Электротехнический университет
Кафедра систем информатики
Дисциплина «Дискретная математика»
ОТЧЕТ
по лабораторной работе № 3
на тему «Задача коммивояжера»
Выполнил: студент гр. д 664 Болсобоев Б.Е.
Проверила: __________________________
Улан-Удэ
2014
Словесная постановка задачи
Задача коммивояжёра состоит в следующем:
Коммивояжер (бродячий торговец) должен выйти из первого города (графа), посетить по разу
в неизвестном порядке города 2,1,3.. n и вернуться в первый город. Расстояния между городами
известны. В каком порядке следует обходить города, чтобы замкнутый путь (тур) коммивояжера
был кратчайшим?
1. Формальная постановка задачи
Входные данные: Натуральное число N (1<=N<=16) и квадратная матрица D порядка N,
содержащая длины дорог между всеми парами городов. Если какой-либо дороги не существует,
соответствующий элемент матрицы равен нулю.
Выходные данные: Минимальная длина пути.
Алгоритм решения задачи
Пусть D(i,j) – длина дуги из вершины i в вершину j, V — множество вершин графа, а E —
множество дуг. Обозначим за T(i,j) минимальный вес некоторого пути, начинающегося в вершине
0, заканчивающегося в вершине j и проходящего только по вершинам из множества i (причём
ровно по одному разу по каждой). Если такой путь отсутствует, положим T(i,j)=∞ (при реализации
в качестве бесконечности следует выбрать значение, которое заведомо превосходит все
возможные длины путей). Случай, когда подмножество состоит из самой вершины 0, тривиален.
Можно выделить также несколько случаев, когда искомого пути гарантированно не существует.
Во-первых, в соответствии с выбранными подзадачами, вершины 0 и j должны принадлежать
множеству i. Нарушение этого условия автоматически ведёт к тому, что решения у подзадачи нет.
Во-вторых, если j=0, то путь должен состоять из одной вершины (в противном случае мы бы
посетили вершину 0 дважды, что недопустимо). Но если при этом множество i помимо нулевой
содержит и другие вершины, то подзадача решения не имеет. Рекуррентные соотношения
основаны на том, что искомый путь должен состоять из оптимального пути из 0 в некоторую
вершину k и ребра k(i,j). При этом, поскольку вершина j является концевой вершиной текущего
пути, то во вспомогательном пути она встречаться не должна.
Итак, для каждого j мы знаем вес минимального гамильтонова пути, начинающегося в 0 и
заканчивающегося в j. Тогда, чтобы получить гамильтонов цикл, достаточно к пути добавить
дугу (j,0) (если, конечно, она есть в графе). Из всех таких циклов нужно выбрать наименьший по
весу. Для того чтобы иметь возможность восстановить сам цикл, необходимо при решении
подзадач сохранять номер предпоследней вершины в оптимальном пути.
2. Листинг программы
#include <iostream>
using namespace std;
const int inf=1E9,NMAX=16;
int n,i,j,k,m,temp,ans,d[NMAX][NMAX],t[1<<NMAX][NMAX];
bool get(int nmb,int x)
{ return (x&(1<<nmb))!=0; }
int main()
{
cin >>n;
for (i=0;i<n;++i)
for (j=0;j<n;++j) cin>>d[i][j];
t[1][0]=0; m=1<<n;
for (i=1;i<m;i+=2)
for (j=(i==1)?1:0;j<n;++j)
{
t[i][j]=inf;
if (j>0 && get(j,i))
{
temp=i^(1<<j);
for (k=0;k<n;++k)
if (get(k,i) && d[k][j]>0) t[i][j]=min(t[i][j],t[temp][k]+d[k][j]);
}
}
for (j=1,ans=inf;j<n;++j)
if (d[j][0]>0) ans=min(ans,t[m-1][j]+d[j][0]);
if (ans==inf) cout<<-1; else cout<<ans;
system("pause");
}
5. Тестирование
На рисунке показаны результаты работы программы для n=5.
Тестирование показало корректность программы.
Download