5-5回溯-TSP问题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
TSP:
某售货员到若干城市去推销商品,已知各城市之间的路程(或旅费)。
他要选定一条路线,经过每个城市一遍最后回到驻地的路线,使得总的路程(或总旅费)最小。
设G=(V, E)是一个带权图。
图中各边的费用(权值)为正。
图中一条周游路线是包括V 中所有顶点的回路。
一条周游路线的费用是该路线上所有边的费用之和。
所谓旅行售货员问题就是要在图G中找出一条最小费用的周游路线。
Try(s){
做挑选候选者的准备;while (未成功且还有候选者){挑选下一个候选者next ;if (next 可接受) {记录next ;if (满足成功条件) {成功并输出结果}
else Try(s+1);if (不成功) 删去next 的记录; }}return 成功与否}线路上的第s 个结点这里只需依次考察n–1个结点,即for (i=2; i<=n; i++)下一个候选就是i 结点i 尚未在路线中且总耗费值不大。
就是将结点i 放入路线中并修改耗费值s = = n 若新线路更好,就用新线路取代老线路。
这里无所谓成功与否,因为每一条周游路线都要进行比较。
Try(s){
for (i= 2; i<= n; i++)
if (Accept(i)) {
Record(s, i);
if (s = = n)
if (better) TakeNewPath( ) ;
else Try(s+1);
Move-off(s, i)}
return }
用数组C[n][n]存放n个城市间的费用值。
用数组P[n]记录已经找到的费用最小的周游
路线,C为其相应的费用,C初值为∞。
用数组N[n]记录目前正在寻找的周游路线,
NC为其相应的费用;
如果NC+C[N[n]][1]小于C,则路线N更佳,
于是P[] = N[],C = NC+C[N[n]][1]。
如果结点next不是N中已有的结点,且C大于NC+C[N[i]][next],则结点next是可接受的。
Try(s){
for (i = 2; i <= n; i++)
if (Accept(i)) {Record(s, i);
if (s = = n)
if (better) TakeNewPath( ) ;else Try(s+1);
Move-off(s, i)}
return }
(C> NC+C[N[s]][i] &&T[i]) {
数组T[]表示结点i 尚未被选(C >NC+C[N[s]][1]) TakeNewPath( );
Record(s, i);
{NC = NC + C[N[s]][i]; T[i] = 0; N[s] = i; } Move-off(s, i);
{NC = NC –C[N[s]][i]; T[i] = 1; N[s] = 0;}
TakeNewPath( )
{ for (i=1; i<=n; i++) P[i] = N[i];
C = NC + C[N[s]][1];}
TSP(n, C[n][n]) {
for (i= 1; i<= n; i++)
{N[i]=0; P[i] = 0; T[i] = 1}
NC = 0;C = ∞; N[1]=1; T[1] = 0; Try(1);
Output(P);
}
为了便于迭代程序中回溯的判断,我们将城市结点编号为1 ~ n,用编号0作为末尾的标记。
先回顾一下采用末尾标记的迭代回溯法:
Backtrack(Tree T) {unfinish = true;L.Push(T.root); while (unfinish || L≠Φ) {a = L.Pop( ); if (a is the last mark) backastep( );else if (a is good) {Record(a);if (a is goal) {unfinish = false; output( );}
else if (a has sons) L.Push-Sons(a) else Move-off(a);}}}要比较所有路径,无需此句L≠Φ ) {初始化后压入除1外的n-1个结点。
N[1] = 1; j = 1; L.Push-Sons(2,…,n,0);末尾标记为0。
(a = = 0) backastep( );
删除第j 个结点,然后j––.{Move-off(N[j–1], N[j]); j––;}a 不在路线中且新增后的费用仍低。
(T[a]&&C–NC>C[N[j]][a]){Record(N[j],a);
已经n 个结点且新路线的费用更低。
这个判断不需要了(j = = n ) {if (C >NC+C[a][1]) {TakeNewPath( ); Move-off(N[j], a);}}else {j++; L.Push-Sons(2,…,n,0)}}}
Backtrack(n, C) {
j = 1; N[j] = 1; L.Push-Sons(2,…,n,0);
while (L≠Φ) { a = L.Pop( );
if (a = = 0) {Move-off(N[j–1], N[j]); j––;} else if (T[a] && C –NC > C[N[j]][a])
{Record(N[j], a);
if (j = = n )
{if (C >NC+C[a][1])
{TakeNewPath( ); Move-off(N[j], a);}} else {j++; L.Push-Sons(2,…,n,0)}}}
Record(N[j],a);
{NC = NC + C[N[j]][a]; T[a] = 0; N[j+1] = a; } Move-off(N[j],a);
{NC = NC –C[N[j]][a]; T[a] = 1; N[j+1] = 0;} TakeNewPath( ) {
for (i=1; i<=n; i++) P[i] = N[i];
C = NC + C[N[j]][1];}
TSP(n, C[n][n]) {
for (i = 1; i <= n; i++)
{N[i]=0; P[i] = 0; T[i] = 1}
NC = 0;C = ∞; N[1]=1; T[1] = 0; Backtrack(n, C);
Output(P);
}
123显然TSP 是一个求排列的问题。
求排列:确定n 个元素的满足某种性质的排列。
其搜索树称为排列树,通常有n!个叶结点,因此遍历排列树需要时间为O(n!)。
所以TSP 问题的时间复杂性为O(n!)。