HDU 2224 The shortest path (TSP模板)

来源:互联网 发布:ubuntu.com 编辑:程序博客网 时间:2024/05/16 04:57

题目大意:

给定平面上n个点的坐标,进行一次遍历,遍历的规则就是先从最左边的点遍历到最右边的点,然后从最右边的点遍历到最左边的点,而且所有点要遍历完。

算法:这题完全就是双调旅途的模板

求解过程:

(1)首先将各点按照x坐标从小到大排列,时间复杂度为O(nlgn)。

(2)寻找子结构:定义从Pi到Pj的路径为:从Pi开始,从右到左一直到P1,然后从左到右一直到Pj。在这个路径上,会经过P1到Pmax(i,j)之间的所有点且只经过一次。

在定义d(i,j)为满足这一条件的最短路径。我们只考虑i>=j的情况。

同时,定义dist(i,j)为点Pi到Pj之间的直线距离。

(3)最优解:我们需要求的是d(n,n)。

关于子问题d(i,j)的求解,分三种情况:

A、当j < i - 1时,d(i,j) = d(i-1,j) + dist(i - 1,i)。

由定义可知,点Pi-1一定在路径Pi-Pj上,而且又由于j<i-1,因此Pi的左边的相邻点一定是Pi-1.因此可以得出上述等式。

B、当j = i - 1时,与Pi左相邻的那个点可能是P1到Pi-1总的任何一个。因此需要递归求出最小的那个路径:

d(i,j) = d(i,i-1) = min{d(k,j) + dist(i,k)},其中1 <= k <= j。

C、当j=i时,路径上最后相连的两个点可能是P1-Pi、P2-Pi...Pi-1-Pi。

因此有:

d(i,i) = min{d(i,1)+dist(1,i),...,d(i,i-1),dist(i-1,i)}.。


#include <set>#include <map>#include <queue>#include <deque>#include <cmath>#include <vector>#include <string>#include <cstdio>#include <cstdlib>#include <cstring>#include <cassert>#include <iostream>#include <algorithm>using namespace std;#define L(i) i<<1#define R(i) i<<1|1#define INF  0x3f3f3f3f#define pi acos(-1.0)#define eps 1e-9#define maxn 1000010#define MOD 1000000007#define zero(x) (((x)>0?(x):-(x))<eps)int n;int x[220],y[220];double dis[220][220];double dp[220][220];int main(){    while(scanf("%d",&n) != EOF)    {        for(int i = 1; i <= n; i++)            scanf("%d%d",&x[i],&y[i]);        for(int i = 1; i <= n; i++)            for(int j = 1; j <= n; j++)                dis[i][j] = sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));        dp[2][1] = dis[1][2];        for(int i = 3; i <= n; i++)            for(int j = 1; j < i; j++)        {            if(i - 1 > j)                dp[i][j] = dp[i-1][j] + dis[i-1][i];            else            {                dp[i][j] = 10000000;                for(int k = 1; k < j; k++)                    dp[i][j] = min(dp[i][j],dp[j][k]+dis[k][i]);            }        }        dp[n][n] = dp[n][n-1] + dis[n][n-1];        printf("%.2f\n",dp[n][n]);    }    return 0;}



0 0