poj 2677 双调旅行商dp

题意:

题意就是双调旅行商。

John uses the following strategy: he starts from the leftmost point, then he goes strictly left to right to the rightmost point, and then he goes strictly right back to the starting point. It is known that the points have distinct x-coordinates.

这种旅程即为从最左点开始,严格地从左到右直至最右点,然后严格地从右到左直至出发点,并且每个x是独一无二的。

然后求最小距离的花费。


解析:

先将坐标按照x轴从小到大排序。

dp[i][j]代表从 i 到 j 的最小花费。

定义从Pi到Pj的路径为:从Pi开始,从右到左一直到P1,然后从左到右一直到Pj。

在这个路径上,会经过P1到Pmax(i,j)之间的所有点且只经过一次。

首先,若 1 <=  i < j - 1:

如上图,此时 dp[ i ] [ j ] = dp[ i ] [ j - 1] + dist( j - 1, j )。

这个很好理解,直接到点 1 然后点 1 到 j-1 然后到j的距离。


其次,当 i == j - 1,   1 <= k < j - 1

此时,若还像情况一那样连接的话,点 j-1 和点 j 将会直接连成闭合线,直接忽视n,这种情况不存在。

所以,此时从前面的点1 ~ j - 2 这些点中找到一个满足条件最小的点k, 此时k + 1 ~ j - 2的点全部被放到了下面,然后k直接和点j相连。

即:dp[ i ] [ j ] = dp[ j - 1] [ j ] =  min( dp[j - 1] [ k ] + dist(k, j)  );

最后再加上闭环 dp[n - 1][n] + dist(node[n - 1], node[n]); 就行了。


代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#include <climits>
#include <cassert>
#define LL long long
#define lson lo, mi, rt << 1
#define rson mi + 1, hi, rt << 1 | 1

using namespace std;
const int maxn = 520;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const double pi = acos(-1.0);
const double ee = exp(1.0);

struct Node
{
    double x, y;
} node[maxn];

int n;
double dp[maxn][maxn];

bool cmp(Node a, Node b)
{
    return a.x < b.x;
}

double dist(Node a, Node b)
{
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

double DP()
{
    dp[1][2] = dist(node[1], node[2]);
    for (int j = 3; j <= n; j++)
    {
        //i < j - 1
        for (int i = 1; i < j - 1; i++)
        {
            dp[i][j] = dp[i][j - 1] + dist(node[j - 1], node[j]);
        }
        // i = j - 1
        dp[j - 1][j] = inf;
        for (int k = 1; k < j - 1; k++)
        {
            double t = dp[k][j - 1] + dist(node[k], node[j]);
            if (t < dp[j - 1][j])
            {
                dp[j - 1][j] = t;
            }
        }
    }
    return dp[n - 1][n] + dist(node[n - 1], node[n]);
}

int main()
{
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
#endif // LOCAL
    while (~scanf("%d", &n))
    {
        for (int i = 1; i <= n; i++)
        {
            scanf("%lf%lf", &node[i].x, &node[i].y);
        }
        sort(node + 1, node + n + 1, cmp);
        printf("%.2lf\n", DP());
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值