【算法导论07】回溯法-旅行售货员问题

07回溯法-旅行售货员问题

问题描述:
已知有m个城市,城市之间由n条不同长度的道路相连。一个售货员从一座城市出发,途径所有城市,并最终回到原点,设计算法计算售货员所走的最短路径结点。

问题分析:
从问题不难看出,此问题属于图中的最小哈密顿图问题。最直接的方法便是遍历找出所有能走完全部结点的道路,并选择其中最短的一条路作为答案。已知图的遍历方式有两种,深度遍历和广度遍历。而排列算法有perm算法,因此此处使用深度遍历+perm算法来寻找最小哈密顿图,这两种算法的组合便是回溯法:通过深度搜索来寻找答案,当发现其中一条道路行不通时,遍选择下一条路继续进行。

算法描述:
最短路径 = 已经走过的最短路径 + 当前要走的最短路径 + 以后要走的最短路径。
perm全排列算法流程:
1)交换A[0]和A[i]
2)对除A[0]以外元素进行perm()递归。
3)再次交换A[0]和A[i],防止下次执行顺序被打乱,出现重复排序。
4)对以上算法进行基于顶点数i的循环
(perm全排序算法具体讲解见:06递归算法-perm算法)

下面讲述如何通过对perm算法的改进实现回溯法:
由于该问题的解答需要获取到顶点结点,所以当perm算法中第i个结点完成交换后,在对从i+1到末尾顶点进行全排列之前,要获取到前一个边的长度,即在流程1与2中间加一个 cc+=A[i-1][i](cc表示已走的路,随着递归的进行而不断增加)。而当第2步实现以i为首的顶点路径集合的全排序后,由于需要回溯到i结点处(选择以i+1结点进行全排序),所以还需将之前增加的结点路径减去以实现回溯,即在流程2和3之间插入一句 cc-=A[i-1][i]。至此,对perm算法的全部改进主体工作完成。

笔记:(-from Tsinghua University算法导论课程)
【算法导论07】回溯法-旅行售货员问题_第1张图片
【算法导论07】回溯法-旅行售货员问题_第2张图片

旅行售货员问题代码:

void traveling(int i){
	if(i==n){//一次递归到底,结束当前递归
		if(A[x[i-1]][x[i]]!=Noedge && A[x[i]][x[1]]!=NULL && (cc+A[x[i-1]][x[i]]+A[x[i]][x[1]]<best||best == NULL) ){
			best = cc+A[x[i-1][x[i]]] + A[x[i]][x[1]];
			iscycle = true;
			for(int j=1;j<=n;j++)
				bestx[i] = x[j];
		}
	}else{
		for(int j=i;j<=n;j++){//对所有节点进行全排序
			if(A[x[i-1]][x[j]]!=Noedge && (cc+A[x[i-1]][x[j]]<best||best == Nodege)){//只有边存在且已走过的路经+当前要走的路径<最短路径时,才会执行语句,对后面路径进行全排列(注意和第一个if判断语句的区别)
			swap(x[i],x[j]);
			cc+=A[x[i-1]][x[i]];//新增边
			traveling(i+1);
			cc-=A[x[i-1]][x[i]];//回溯减去边
			swap(x[i],x[j]);
			}
		}
	}
}

20201010补充:
该算法本质是二叉树的深度遍历,而之所以使用到了perm算法对数字进行全排序,是因为该问题本身结构是由图构成,而后转化成二叉树,该二叉树并没用事先建立,也没有对图进行邻接矩阵设计,因此采用perm算法进行设计,本质是构造二叉树。
20201014补充:回溯法的构造解空间树分为排列树和子集树。当选项可以重复时,则解空间树为子集树,如01背包中,每项都可以选或者不选,最小重量机器设计中,每个零件都可以在所有供应商买到。当解空间不可以重复时,解空间树则为排列树,如旅行售货员问题中,每条道路不能重复走,羽毛球最佳配对问题中,队员不能重复选择。

回溯法设计步骤:
1 根据问题确定解空间树类型(子集树,排列树)以及分支情况。
2 确定显式约束(背包容量、总价格)和隐式约束(最大价值、最小重量),以设计限界函数。
3 确定需要更新的变量以及问题的解。

你可能感兴趣的:(回溯法,算法导论,c语言,哈密顿路径)