旅行员售货问题
问题描述
某售货员要到若干城市去推销商品,已知各城市之间的路程(旅费),他要选定一条从驻地出发,经过每个城市一遍,最后回到驻地的路线,使总的路程(总旅费)最小。
问题分析
旅行售货员问题的解空间是一棵排列树。对于排列树的回溯法与生成1,2,……n的所有排列的递归算法Perm类似。开始时x=[1,2,……n],则相应的排列树有x[1:n]的所有排列构成。
在递归算法Backtrack中,当i=n时,当前扩展节点是排列树的叶节点的父节点。此时算法检测图G是否存在一条从顶点x[n-1]到顶点x[n]的边和一条从顶点x[n]到顶点1的边。如果这两条边都存在,则找到一条旅行员售货回路。此时,算法还需要判断这条回路的费用是否优于已找到的当前最优回流的费用bestc。如果是,则必须更新当前最优值bestc和当前最优解bestx。
当i<n时,当前扩展节点位于排列树的第i-1层。图G中存在从顶点x[i-1]到顶点x[i]的边时,x[1:i]构成图G的一条路径,且当x[1:i]的费用小于当前最优值时算法进入树的第i层,否则将剪去相应的子树。
代码
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#define NO_PATH -1 //没有通路
using namespace std;
int N;
int **City_Graph; //保存图信息
int *x; //x[i]保存第i步遍历的城市
int bestw; //最优路径总权值
int cw; //当前路径总权值
int bw; //深度搜索完成总权值
int *bestx; //最优路径
void Travel_Backtrack(int t)
{//递归法
int i,j;
if(t==N)
{
if(City_Graph[x[N-1]][x[N]] != NO_PATH && City_Graph[x[N]][1] != NO_PATH && (cw + City_Graph[x[N-1]][x[N]] + City_Graph[x[N]][1] < bestw || bestw == NO_PATH)) //判断当前路径是否是更优解
{
for (i=1;i<=N;i++)
{
bestx[i] = x[i];
}
bestw = cw + City_Graph[x[N-1]][x[N]]+City_Graph[x[N]][1];//计算总权值(非最优)
}
}
else
{
for(j=t;j<=N;j++)// 搜索子树
{ // 是否可进入x[j]子树?
if(City_Graph[x[t-1]][x[j]] != NO_PATH && (cw+City_Graph[x[t-1]][x[j]]<bestw||bestw==NO_PATH))//剪枝条件:可达且未加入到路径中且当前总权值小于最优权值
{
swap(x[t],x[j]);
cw += City_Graph[x[t-1]][x[t]];
Travel_Backtrack(t+1);
cw -= City_Graph[x[t-1]][x[t]];
swap(x[t],x[j]);
}
}
}
}
int main()
{
int i;
// 图的节点个数
scanf("%d",&N);
City_Graph = (int **)malloc((N+1)*sizeof(int *));
x = (int *)malloc((N+1)*sizeof(int));
bestx = (int *)malloc((N+1)*sizeof(int));
for(i=0;i<=N;i++)
City_Graph[i] = (int *)malloc((N+1)*sizeof(int));
//建立图(邻接矩阵)
for(i=1;i<=N;i++)
for(int j=1;j<=N;j++)
scanf("%d",&City_Graph[i][j]);
//测试递归法,初始化
for (i=1;i<=N;i++)
{
x[i] = i;
bestx[i] = 0; //还没有最优解
}
x[1] = 1; //第一步 走城市1
bestw = NO_PATH;
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;
}
/*
4
0 30 6 4
30 0 5 10
6 5 0 20
4 10 20 0
*/
#include <stdio.h>
#include <stdlib.h>
#define NO_PATH -1 //没有通路
#define MAX_WEIGHT 1073741824
int N;
int **City_Graph; //保存图信息
int *x; //x[i]保存第i步遍历的城市
int *isIn; //保存 城市i是否已经加入路径
int bestw; //最优路径总权值
int cw; //当前路径总权值
int bw; //深度搜索完成总权值
int *bestx; //最优路径
//-----------------------------------------------------------------
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]; //计算总权值(非最优)
if(bw < bestw) //挑选出最优总权值
{ //判断当前路径是否是更优解
for (i=1;i<=N;i++)
{
bestx[i] = x[i];
}
bestw = bw;
}
}
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()
{
int i;
scanf("%d",&N);
City_Graph = (int **)malloc((N+1)*sizeof(int *));
x = (int *)malloc((N+1)*sizeof(int));
isIn = (int *)malloc((N+1)*sizeof(int));
bestx = (int *)malloc((N+1)*sizeof(int));
for(i=0;i<=N;i++)
City_Graph[i] = (int *)malloc((N+1)*sizeof(int));
//建立图(邻接矩阵)
for(i=1;i<=N;i++)
for(int j=1;j<=N;j++)
scanf("%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;
}
/*
4
0 30 6 4
30 0 5 10
6 5 0 20
4 10 20 0
*/