旅行售货员问题

旅行售货员问题

【题目】

       某售货员要到4个城市去推销商品,已知各城市之间的路程,如右图所示。

请问他应该如何选定一条从城市1出发,经过每个城市一遍,最后回到城市1的路线,使得总的周游路程最小?并分析所设计算法的计算时间复杂度。

【分析】

       该题利用回溯法求解,此时需要确定解空间的类型:我们可以知道该解空间为一棵排列树。我们假设初始的一条路线为xx中的值为 123,……,n,表示该路线为由城市1依次经过城市23……到n后再回到城市1(当然是假设该路线存在)。如果不存在的话,我们只需改变一下这个排列的排列方式,再进行判断,所以可想而知,我们可以知道该解空间是一棵排列树。

       当然上述的说法,只是单纯的穷举,这并不是我们想要的回溯法,我们通过递归实现,在递归的过程中适当地“剪枝”即除去那些不可能形成最优解的解。现在我们来确定一下可行的约束条件,当我们进行递归搜索,搜索到第t层时,我们需要判断一下x[t]所代表的城市是否与上一层x[t-1]所代表的城市有“路”,如果没有的话,需要改变x[t]的值,然后继续上述判断,当出现一个满足条件的x[t]后还要判断当前从1t-1所走的路程cc加上x[t]x[t-1]的距离是否小于当前已经记录的最优解(最优解的初始值是一个足够大的数),如果到t的距离比当前最优解还要大的话,那么再以这条路线搜索下去的话回到城市1的路程一定比当前最优解还大,所以我们没有必要对这条路线进行下一步的搜索。最后我们来确定当搜索到叶子结点的时候我们该如何处理?已知搜索到t层时,若t = n,说明已经搜索到了叶子结点,这个时候我们还需做上述所说的两个判断,如果两个判断都通过的话,说明该解比当前最优解还优,那么我们需要将该解记录下来,并记录该解的最优值。

【伪代码】

void travel(int t) {

    if(t到达第n层即搜索到叶子结点) {

        if(城市x[t-1]可以到达城市x[t],并且城市x[t]可以回到城市1,且此时所走的路程cc加上

            x[t-1]x[t]的距离和x[t]1的距离小于当前最优值bestc) {

                将最优解记录下来;

                将最优值记录下来;

            }

        return;

    }

    for(int i = t; i < n; i++) {

        if(城市x[t-1]能达到城市x[i]即这两个城市间有边,并当前所走的路程cc加上这两个城市的距离

        没有比当前最优值bestc) {

        swap(x[i], x[t]);

        修改此时所走的路程cc;

        进入下一层递归;

        恢复原来cc的值;

        swap(x[i], x[t]);

        }

    }

}

【程序】

用C++语言编写程序,代码如下:

#include<iostream>
using namespace std;

const int INF = 10000000;
int n, cc = 0, bestc = INF;
int **g;
int *x, *bestx;

void travel(int t) {
	if (t == n) {
		if (g[x[t - 1]][x[t]] != INF && g[x[t]][1] != INF &&
			(cc + g[x[t - 1]][x[t]] + g[x[t]][1] < bestc || bestc == INF)) {
			for (int i = 0; i < n + 1; i++)
				bestx[i] = x[i];
			bestc = cc + g[x[t - 1]][x[t]] + g[x[t]][1];
		}
		return;
	}

	for (int i = t; i < n; i++) {
		if (g[x[t - 1]][x[i]] != INF && (cc + g[x[t - 1]][x[i]] < bestc
			|| bestc == INF)) {
			swap(x[i], x[t]);
			cc += g[x[t - 1]][x[t]];
			travel(t + 1);
			cc -= g[x[t - 1]][x[t]];
			swap(x[i], x[t]);
		}
	}
}

void output() {
	cout << bestc << endl;
	cout << bestx[1];
	for (int i = 2; i < n + 1; i++)
		cout << " " << bestx[i];
	cout << " " << bestx[1] << endl;
}

int main() {

	n = 4;
	g = new int*[n + 1];
	x = new int[n + 1];
	bestx = new int[n + 1];

	for (int i = 0; i < n + 1; i++) {
		g[i] = new int[n + 1];
		x[i] = i;

		for (int j = 0; j < n + 1; j++)
			g[i][j] = INF;
	}

	g[1][2] = g[2][1] = 30;
	g[1][3] = g[3][1] = 6;
	g[1][4] = g[4][1] = 4;

	g[2][3] = g[3][2] = 5;
	g[2][4] = g[4][2] = 10;

	g[3][4] = g[4][3] = 20;

	travel(2);
	output();

	return 0;
}
【结果】

先设置好城市间的距离,调用回溯方法,输出最优值(最小路程)和最优解(路线):

该算法的时间复杂度为O(n!)

  • 19
    点赞
  • 97
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值