dp专题-UVA - 1347-双调欧几里得旅行商问题-不重复的来回路径问题

题目大意:
双调欧几里得旅行商问题(动态规划)
给定平面上n(n≤1000)个点的坐标(按照x递增的顺序给出。各点x坐标不同,且均为 正整数),你的任务是设计一条路线,从最左边的点出发,走到最右边的点后再返回,要求 除了最左点和最右点之外每个点恰好经过一次,且路径总长度最短。两点间的长度为它们的 欧几里德距离,如图9-4所示。
在这里插入图片描述

正确的问题应该是这个:

双调欧几里得旅行商问题是一个经典动态规划问题。《算法导论(第二版)》思考题15-1和北京大学OJ2677都出现了这个题目。

旅行商问题描述:平面上n个点,确定一条连接各点的最短闭合旅程。这个解的一般形式为NP的(在多项式时间内可以求出)

J.L. Bentley 建议通过只考虑 双调旅程(bitonictour)来简化问题,这种旅程即为从最左点开始,严格地从左到右直至最右点,然后严格地从右到左直至出发点。 下图(b)显示了同样的7个点的最短双调路线。在这种情况下,多项式的算法是可能的。事实上,存在确定的最优双调路线的O(n*n)时间的算法。

上图中,a是最短闭合路线,这个路线不是双调的。b是最短双调闭合路线。

"从左到右再回来"不太方便思考,可以改成:两个人同时从最左点出发,
沿着两条不同 的路径走,最后都走到最右点,必须严格的从左往右走(x增大的方向)。
且除了起点和终点外其余每个点恰好被一个人经过

定义d(i,j)表示1~max(i,j)全部走过,且两个人的当前位置分别是i和j,还需 要走多长的距离。不难发现d(i,j)=d(j,i),因此从现在开始规定在状态中i>j。这样,不管是 哪个人,下一步只能走到i+1, i+2,…这些点。可是,如果走到i+2,情况变成了“1~i和i+ 2,但是i+1没走过”,无法表示成状态!怎么办?禁止这样的决策!也就是说,只允许其中 一个人走到i+1,而不能走到i+2, i+3,…。换句话说,状态d(i,j)只能转移到d(i+1,j)和d(i+ 1,i)。
在这里插入图片描述
可是这样做产生了一个问题:上述“霸道”的规定是否可能导致漏解呢?不会。因为如果 第一个人直接走到了i+2,那么它再也无法走到i+1了,只能靠第二个人走到i+1。既然如 此,现在就让第二个人走到i+1,并不会丢失解。
边界是d(n-1,j)=dist(n-1,n)+dist(j,n),其中dist(a,b)表示点a和b之间的距离。
在这里插入图片描述
因为根据 定义,所有点都走过了,两个人只需直接走到终点。所求结果是dist(1,2)+d(2,1),因为第一 步一定是某个人走到了第二个点,根据定义,这就是d(2,1)。

思考:这个状态表示非常巧妙。

#include <bits/stdc++.h>
using namespace std;

double x[1005], y[1005];
double dp[1005][1005];
int n;
double dist(int i, int j)
{
    return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}

double dfs(int i, int j)
{
    if(dp[i][j])
    {
        return dp[i][j];
    }
    if(i==n-1)//边界条件
    {
        return dp[i][j]=dist(n-1, n)+dist(j, n);
    }

    return dp[i][j]=min(dfs(i+1, j)+dist(i, i+1), dfs(i+1, i)+dist(j, i+1));
}

int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%lf%lf",&x[i],&y[i]);
        }
        memset(dp, 0, sizeof(dp));
        printf("%.2f\n",dfs(2, 1)+dist(1, 2));
    }

	return 0;
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值