【小专题】哈密顿回路

哈密顿回路来历:

天文学家哈密顿(William Rowan Hamilton) 提出,在一个有多个城市的地图网络中,寻找一条从 给定的起点 到 给定的终点 沿途恰好经过所有其他城市一次的路径。

这个问题和著名的七桥问题的不同之处在于,七桥问题过桥只需要确定起点,而不用确定终点。

哈密顿回路问题已被证明是NP问题,具有这样性质的问题,很难找到一个有效的算法。实际上对于某些顶点数不到100的网络,利用现有最好的算法和计算机也需要比较荒唐的时间(比如几百年)才能确定其是否存在一条这样的路径。

今天,我们就来看看这类问题是什么样的呢?(“这类问题”说明不一定只是问你是否存在这样一条路径)

由于这是个NP问题,因此我目前的菜鸡水平只能做简化版的。

luogu1523.旅行商简化版

题目背景

欧几里德旅行商(Euclidean Traveling Salesman)问题也就是货郎担问题一直是困扰全世界数学家、计算机学家的著名问题。现有的算法都没有办法在确定型机器上在多项式时间内求出最优解,但是有办法在多项式时间内求出一个较优解。

为了简化问题,而且保证能在多项式时间内求出最优解,J.L.Bentley提出了一种叫做bitonic tour的哈密尔顿环游。它的要求是任意两点(a,b)之间的相互到达的代价dist(a,b)=dist(b,a)且任意两点之间可以相互到达,并且环游的路线只能是从最西端单向到最东端,再单项返回最西端,并且是一个哈密尔顿回路。

 

题目描述

著名的NPC难题的简化版本

现在笛卡尔平面上有n(n<=1000)个点,每个点的坐标为(x,y)(-2^31<x,y<2^31,且为整数),任意两点之间相互到达的代价为这两点的欧几里德距离,现要你编程求出最短bitonic tour。

 

输入输出格式

输入格式:

第一行一个整数n

接下来n行,每行两个整数x,y,表示某个点的坐标。

输入中保证没有重复的两点,

保证最西端和最东端都只有一个点。

 

输出格式:

一行,即最短回路的长度,保留2位小数。

 

输入输出样例

 

输入样例#1:

7
0 6
1 0
2 3
5 4
6 1
7 5
8 2

输出样例#1:

25.58

 

 

这就是哈密顿问题的简化版。它把所有点放在了坐标轴上,并且规定了必须从最左端到最右端再回到最多段(就是规定了方向)。
由于题目中没有给每个点的坐标排序,我们可以先以横坐标x为第一关键字,纵坐标y为第二关键字从小到大排序。
这些点排好序之后,由于规定了方向,问题就转化成了求2条从最左上端(第一个点)到最右下端(最后一个点)的不相交的路径,且这两条路径经过所有点。把这两条路径都看做向右走的即可。
由于规定了方向,如果两条路径分别到达了第i、j个点,那么说明1到max(i,j)号点一定都经过了(因为只能向右走而不能折返,为了经过所有的点,左边的点必须都经过了才能到达本列)。
总结做法:
  f[i][j]表示第一条路径走到第i个点,第二条路径走到第j个点的最短路径,要求i<j(这样max(i,j)就是j,能保证第二条路径一定走在前面),且第1到j个的点都被经过了。这样很容易想到,第j+1个点不是被第一个人走,就是被第二个人走,所以有转移方程
  $f[i][j+1] = \min\{ f[i][j+1], f[i][j]+dis[j][j+1] \}$
  $f[j][j+1] = \min\{ f[i][j+1], f[i][j]+dis[i][j+1] \}$
第一个方程是让第二条路径走到第j+1个点,没啥问题。
第二个方程是让第一条路径走到第j+1个点,但不好理解,且这么走后是不是违反了 第一条路径到达的点i < 第二条路径到达的点j 的原则?但我没有明确指定哪条路径是第一条,哪条路径是第二条呀!因此当第一条路径跑到前面(即第j+1个点)时,我们可以把两条路径“交换”一下,而这样做不会影响状态,相当于第一条路径成了第二条路径,走到了第j个点;而第二条路径成了第一条路径,走到了第j+1个点。如此即可确保第二条路径仍在第一条路径前面了!

 

 1 #include<bits/stdc++.h>
 2 #define maxn 1001
 3 #define inf 1e30
 4 using namespace std;
 5 struct data
 6 {
 7     double x,y;
 8 }g[maxn];
 9 double d[maxn][maxn],f[maxn][maxn];
10 int i,j,k,n;
11 bool cmp(const data &a,const data &b)
12 {
13     if(a.x==b.x) return a.y<b.y;
14     return a.x<b.x;
15 }
16 double mdis(const data &a, const data &b)
17 {
18     return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
19 }
20 int main()
21 {
22     scanf("%d",&n);
23     for(i=1;i<=n;++i) scanf("%lf%lf",&g[i].x,&g[i].y);
24     sort(g+1,g+n+1,cmp);//按横坐标x从小到大排序 
25     for(i=1;i<=n;++i)
26         for(j=i+1;j<=n;++j)
27         {
28             d[i][j]=mdis(g[i],g[j]); //求任意两点的距离 
29             f[i][j]=inf;
30         }
31     f[1][2]=d[1][2];
32     for(i=1;i<n;++i)
33         for(j=i+1;j<=n;++j)
34         {
35             f[i][j+1]=min(f[i][j+1],f[i][j]+d[j][j+1]);
36             f[j][j+1]=min(f[j][j+1],f[i][j]+d[i][j+1]);
37         }
38     printf("%.2lf\n",f[n-1][n]+d[n-1][n]);
39     return 0;
40 }
View Code

 

 

旅行商问题

题目描述

给定一个n个顶点组成的带权有向图的距离矩阵d(i,j)(INF表示没有边)。要求从顶点0出发,经过每个顶点恰好一次后再回到顶点0.问所经过的边的总权重的最小值是多少?

输入样例#1:

7
0 6
1 0
2 3
5 4
6 1
7 5
8 2

输出样例#1:

25.58

 

posted @ 2018-03-01 22:20  大本营  阅读(1156)  评论(0编辑  收藏  举报