-
问题描述:
这个问题要求找出一条n个给定的城市间的最短路径,使我们在回到触发的城市之前,对每个城市都只访问一次。
-
图解:
-
解决方法:
回溯法就是蛮力算法,把所有的可能性都试一次,从里面找出来最优解。这里用的是子集树。因为不能一个城市去多次,所以加一个城市的标记,初始值都是0,如果去过就变成1。
-
代码:
/*
回溯法(子集树)
*/
#include <stdio.h>
# include <stdlib.h>
# include <time.h>
#define N 200 //城市数目
#define NO_PATH -1 //没有通路
#define MAX_WEIGHT 4000
int City_Graph[N+1][N+1]; //保存图信息
int x[N+1]; //x[i]保存第i步遍历的城市
int isIn[N+1]; //保存 城市i是否已经加入路径
int bestw; //最优路径总权值
int cw; //当前路径总权值
int bw; //深度搜索完成总权值
int bestx[N+1]; //最优路径
//-----------------------------------------------------------------
void Travel_Backtrack(int t)
{ //递归法
int i,j;
if(t>N)
{ //走完了,输出结果
for(i=1;i<=N;i++) //输出当前的路径
printf("%d ",x[i]);
printf("\n");
bw = cw + City_Graph[x[N]][1]; //返回起点,需要加上从第n个到第一个的距离
if(bw < bestw) //挑选出最优总权值
{
for (i=1;i<=N;i++)
{
bestx[i] = x[i]; //把每一步的路径保存下来
}
bestw = bw; // 更新最优解
}
return;
}
else
{
for(j=2;j<=N;j++)//因为出发点为1号,所以后面回溯时1号不满足后面的if条件
{ //找到第t步能走的城市
if(City_Graph[x[t-1]][j] != NO_PATH && !isIn[j] /*&& cw<bestw*/)//剪枝条件:可达且未加入到路径中且当前总权值小于最优权值
{
isIn[j] = 1; // 标记走过了
x[t] = j;
cw += City_Graph[x[t-1]][j];
Travel_Backtrack(t+1);
isIn[j] = 0;
x[t] = 0;
cw -= City_Graph[x[t-1]][j];
}
}
}
}
int main(void)
{
int i;
int j;
srand((unsigned)time(NULL));
for(i = 1; i <= N; i++) {
for(j = 1; j <= N; j++) {
int num = 0;
num = rand() % 201;
City_Graph[i][j] = num;
printf("%d ", City_Graph[i][j]);
}
}
//测试递归法,初始化
for (i=1;i<=N;i++)
{
x[i] = 0; //表示第i步还没有解
bestx[i] = 0; //还没有最优解
isIn[i] = 0; //表示第i个城市还没有加入到路径中
}
x[1] = 1; //第一步 走城市1
isIn[1] = 1; //第一个城市 加入路径
bestw = MAX_WEIGHT; // 因为要求最小值先设置为极大值
cw = 0; // 用于保存每一种可能的路径和值
Travel_Backtrack(2); //从第二步开始选择城市
printf("最优值为%d\n",bestw);
printf("最优解为:\n");
for(i=1;i<=N;i++)
{
printf("%d ",bestx[i]);
}
printf("\n");
return 0;
}