双调(欧几里得)旅行商问题
欧几里得旅行商问题是指在一个平面上有n个点,找出连接所有点的最短闭合路径的问题。这个问题虽然是一个求最短路的问题,但是其实和图论没什么很大关系,用的是动态规划的思想。
J.L. Bentley 建议通过只考虑双调(Bitonic)路径来求得最短路径。双调路径是指从一个最左端的点出发,严格向右端行进(x左 < x右)直至到达最右端,然后再从最右端开始向最左端行进直至起点。这样形成的一个路径为双调路径。
如图a,b都是最短路径,但是a为非双调最短闭合路径,b为双调最短闭合路径。
将问题转化为求双调最短路径后,该问题的算法复杂度就变成了O(n^2)。
在利用双调TSP来解决问题时,我们暂且设没有任何两点x坐标相同。
1.将平面上所有点按横坐标x升序排序(O(nlogn))
2.子结构:我们定义d[i][j] (i>=j)为i经过起点到达j点的路径长度,i到起点x单调变小,起点到j单调变大,d[n][n]即为答案。定义dist[i][j] 为i<->j之间的直接距离。
3.DP时会有三种情况
当j < i-1时 d[i][j] = d[i][i-1] + dist[i][i-1];
当j = i-1时 d[i][j] = min(d[i][j],d[j][k] + dist[i][k]) (1 < = k < j);
当j = i 时 d[i][j] = min(d[i][j],d[j][k] + dist[i][k]) (1< = k < j);
第二和第三种情况可以合并一下。
题意如上,一道典型BTSP的模板题。题目入口
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair <int,int> P;
const int INF = 0x3f3f3f3f;
const int maxn = 200+10;
int n;
struct Point
{
int x,y;
Point (int xi = 0,int yi = 0) : x(xi),y(yi) {}
bool operator < (const Point &a)const
{
return this->x < a.x;
}
};
double Distance(Point a, Point b)
{
int X = a.x - b.x;
int Y = a.y - b.y;
return sqrt(X*X+Y*Y);
}
Point p[maxn];
void input()
{
for(int i = 0; i < n; i++)
scanf("%d%d",&p[i].x, &p[i].y);
}
double dist[maxn][maxn];
void Build()
{
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
dist[i][j] = dist[j][i] = Distance(p[i],p[j]);
}
double d[maxn][maxn];
void solve()
{
sort(p,p+n);//排序
//cout << p[0].x << endl;
Build();//建图
//初始化数组
for(int i = 0; i < n; i++)
for(int j = 0; j <n; j++)
d[i][j] = (double)INF;
d[0][0] = 0;
d[0][1] = d[1][0] = dist[0][1];
for(int i = 0; i < n; i++)
for(int j = 0; j <= i; j++)
{
if(j < i-1)
d[i][j] = d[i-1][j] + dist[i][i-1];
else
{
for(int k = 0; k < j; k++)
d[i][j] = min(d[i][j],d[j][k] + dist[i][k]);
}
//cout << i << j << " " << d[i][j] << endl;
}
// for(int i = 0; i < n-1; i++)
// d[n-1][n-1] = min(d[n-1][n-1],d[n-1][i] + dist[n-1][i]);
}
int main()
{
while(~scanf("%d",&n))
{
input();
solve();
printf("%.2f\n",d[n-1][n-1]);
}
return 0;
}