java 双调旅行商 hamiltonian,双调欧几里得旅行商问题

下面讲解公式的由来,最短双调旅程P(i,j)在到达终点P[j]之前,正常来说,按照双调的概念,一定经过了一个其x坐标刚好比点P[j]小的点,也就是P[j-1](当然当i=j-1时就另当别论了),所以如果当i<><><>< p="">

<><><>

这相当于我们每次求解问题P(i,j),都作了一次选择,要么是点P[j-1]或是点P[k]作为P[j]的前一个点,其示意图如下:

40201664_2.png

要知道,我们要求解的问题结果是b[n,n],于是

b[n,n]=b[n-1,n]+distance(n-1,n);

这里要注意的一点,在之前的公式中,并不涉及求解类似b[i,i]的值,这里定义了b[i,i]的情况。

除此之外,还涉及到了对最优解的重构的问题。我们将使用一个r[i][j]数组表示子问题P(i,j)在到达终点P[j]之前经过的一个点P[k]对应的k值(仅挨着点P[j]的点),则子问题的解可以组织为其更小的子问题P(i,k)的解加上点P[k]和点P[j]。由之前的解题思路可知,对于问题P(i,j),当i=j-1时,k<><>< p="">

<><>

其实得到的最优解是个闭合旅程,所以从出发后的第一个点与到达之前的一个点的位置是等价的。如闭合旅程是76431257,也可以是75213467。

构造解的过程如下:

每次加入的点总是在序号大的点下,因为问题P(i,j)总是分解为子问题P(i,k),不管k是等于j-1,还是小于j-1,然后确定点P[k]是到达P[j]之前的一个点,这也是问题每次选择的结果。使用一个数组存放序号,一边从0开始,一边从末尾开始。

40201664_3.png

以下是问题的实现:

#include

#include

#include

using namespace std;

#define N 7

struct Point{

double x;

double y;

};

struct Point points[N+1];

double b[N+1][N+1];

int r[N+1][N+1];

double distance(int i,int j);//第i,j点的欧式距离

double Euclidean_TSP();//最短闭合旅程长度

void my_print_path();//打印旅程

void main(int argc, char **argv){

ifstream infile;

infile.open("input.txt");//读入一个有各点坐标的文档

if (!infile)

{

cout<< li=""><>

}

int i=1;

while (infile>>points[i].x>>points[i].y)

{

i++;

}

cout<<>< li=""><><>

my_print_path();

}

double distance(int i,int j){

return sqrt((points[i].x-points[j].x)*(points[i].x-points[j].x)

+(points[i].y-points[j].y)*(points[i].y-points[j].y));

}

double Euclidean_TSP(){

b[1][2]=distance(1,2);//最小的子问题

for (int j=3;j<=N;j++)

{

//i=1时的情况

for (int i=1;i<>< li=""><>

{

b[i][j] = b[i][j-1]+distance(j-1,j);

r[i][j] = j-1;

}

//i=j-1的情况

b[j-1][j] = b[1][j-1]+distance(1,j);//先设初值为k=1时的值

r[j-1][j] = 1;

for (int k=1;k<>< li=""><>

{

double q = b[k][j-1]+distance(k,j);

if (q 

{

b[j-1][j] = q;

r[j-1][j] = k;

}

}

}

b[N][N] = b[N-1][N]+distance(N-1,N);

return b[N][N];

}

void my_print_path(){

int string[N];

string[0]=N;

string[1]=N-1;

int k=N-1;

int left_hand=N-1,right_hand=N,begin=2,end=N-1;

for (int i=N-1,j=N;k!=1;)

{

k=r[i][j];

if (left_hand>right_hand) //比较那边的点的序号大

{

left_hand=k;

string[begin]=k;

begin++;

}else{

right_hand=k;

string[end]=k;

end--;

}

if (i==j-1)

{

j=i;

i=k;

}else if (i<>< li=""><>

{

j=k;

}

}

cout<

for (int index=0;index<>< li=""><>

{

cout<<>< li=""><>

}

cout<<>< li=""><>

}

input.txt:

0 6

1 0

2 3

5 4

6 1

7 5

8 2

运行后:

最短双调闭合旅程长度是:25.584

该旅程是:7643125

其实旅程也可以是7521346

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值