回溯法:旅行售货员问题

回溯法:旅行售货员问题(C++)

  • 问题描述
  • 算法设计
    • 确定问题的解空间
    • 确定解空间的结构
    • 搜索方式和边界条件
  • 代码实现
  • 算法复杂度分析

回溯法:旅行售货员问题

问题描述

某售货员要到n个城市去推销商品,已知各城市之间的路程,请问他应该如何选定一条从城市1出发,经过每个城市一遍,最后回到城市1的路线,使得总的周游路程最小?并分析所设计算法的计算时间复杂度。

算法设计

根据回溯法进行算法设计

确定问题的解空间

用图的邻接矩阵a表示无向连通图G = (V , E)。若(I,j)属于图的边集,则a[i][j] 表示两个城市之间的距离,,若a[i][j] = 0,表示城市之间无通路。问题的解空间就是出发城市+其他城市全排列。

确定解空间的结构

该问题的解空间结构为一颗排列树。

搜索方式和边界条件

搜索方式:深度优先搜索
边界条件:
(1)当i = n时,当前扩展的节点是排列树叶节点的父节点,在此时需要检测是否存在一条从x[i-1]到顶点x[n]的边和一条x[n]到x[1]的边,如果都存在,则找到一条旅行售货员回路。并且还需要判断这条回路的费用是否优于当前最优回路的费用,如果是,必须进行更新。
(2)当i < n时,如果存在路径并且最优值更小,则进行更新,否则剪去相应的子树。

代码实现

template<class Type>
class Traveling {
	friend Type TSP(Type**, Type[], int, Type);
public:
	void Backstack(int i);
	void Swap(int, int);
	int n;
	int *x;
	int *bestx;
	Type **a;
	Type cc;
	Type bestc;
	Type NoEdge;
};

template<class Type>
void Traveling<Type>::Swap(int a, int b) {
	int k;
	k = x[a];
	x[a] = x[b];
	x[b] = k;
}

template<class Type>
void Traveling<Type>::Backstack(int i) {
	if (i == n) {
		if (a[x[n - 1]][x[n]] != NoEdge && a[x[n]][x[1]] != NoEdge && (cc + a[x[n - 1]][x[n]] + a[x[n]][x[1]] < bestc || bestc == NoEdge)) {
			for (int j = 1; j <= n; j++)
				bestx[j] = x[j];
			bestc = cc + a[x[n - 1]][x[n]] + a[x[n]][x[1]];
		}
	}
	else {
		for (int j = i; j <= n; j++) {
			if (a[x[i - 1]][x[j]] != NoEdge && (cc + a[x[i - 1]][x[j]] < bestc || bestc == NoEdge)) {
				Swap(x[i], x[j]);
				cc += a[x[i - 1]][x[i]];
				Backstack(i + 1);
				cc -= a[x[i - 1]][x[i]];
				Swap(x[i], x[j]);
			}
		}
	}
}



template<class Type>
Type TSP(Type **a, Type v[], int n, Type NoEdge) {
	Traveling<Type> Y;
	Y.x = new int[n + 1];
	for (int i = 1; i <= n; i++)
		Y.x[i] = i;
	Y.a = a;
	Y.n = n;
	Y.bestc = NoEdge;
	Y.bestx = v;
	Y.cc = 0;
	Y.NoEdge = NoEdge;
	Y.Backstack(2);
	delete[]Y.x;
	return Y.bestc;
}

算法复杂度分析

最坏情况下,需要更新最优解O((n-1)!)次,每次更新bestx需O(n)计算时间,所以整体事件复杂度为O(n!)。

你可能感兴趣的:(回溯法:旅行售货员问题)