HDU 2224 The shortest path (TSP模板)

本文介绍了一种在平面上寻找最短遍历路径的方法——双调旅途算法。该算法通过将点按x坐标排序并利用动态规划求解,实现从最左到最右再返回的遍历过程,适用于计算机科学中的几何问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:

给定平面上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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值