UVa 1347 Tour

来源:互联网 发布:如何创建域名 编辑:程序博客网 时间:2024/06/06 11:42

来源:UVa 1347、PKU程序设计实习课程练习
题目描述:
给定n个平面上的点的坐标,求出从最左边的点严格向右走到最右边的点,再严格向左走到最左边的点,并且所有点都经过恰好一次的走法,结果输出该走法距离.
题目分析:
首先给郭神的搜索算法跪了,本质上和dp差不到哪里去.我一开始是不会做的,直到今天早上翻到算导动归一章看到描述完全相同的所谓”双调欧几里得旅行商问题”,根据它给的Hint才勉强想了出来
总的来说,先按x坐标从小到大排序.把状态定义成为dp[i][j](i< j)表示从i严格向左走到0点,再严格向右走到j点的最短距离.那么问题可以归结为以下两种子问题:
1.dp[i1][i]=min0<=j<i1{dp[j][i1]+Dist(dots[j],dots[i]} 这是因为按照题目限制的走法,此时0i路径上与i相邻的点可能是0..i-2的任意一个.
2.dp[j][i]=dp[j][i1]+Dist(dots[i1],dots[i])(j<i1)这是因为j<i1时,按照状态定义,即走路线j0i,此时必然会走路线(i - 1) i,因而可以归结为已经计算过的子问题.
最后答案就是dp[n2][n1]+Dist(dots[n2],dots[n1]}这是因为最短路一定包含(n - 2)(n - 1)而按照定义dp[n - 2][n - 1]是去除该连线外的最短路.
此外必须要指明的是动态规划的初始状态和规划方向,初始状态是dp[0][1]=Dist[0][1](这里假设至少有两个点,否则特判一下即可).规划方向是对于dp[u][v],写两重循环,先枚举v:2..n1,固定后对里层枚举u:0..v1.规划的合理性可以作表,查看每个位置要计算时它所依赖的状态按照此规划方向是否已经计算出来.代码具体实现是使用了一点小技巧,提取出两种子问题情况的公共枚举点,缩减了少许代码量.

//  Created by wander on 16/6/11.//  Copyright © 2016年 W4anD0eR96. All rights reserved.#include "bits/stdc++.h"using namespace std;#define x first#define y secondtypedef pair&lt;double, double&gt; P;int n;double Dist(P a, P b) { return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y- b.y)); }int main() {#ifdef DEBUG  freopen("in", "r", stdin);  freopen("out", "w", stdout);#endif  while (~scanf("%d", &n)) {    double res, dp[60][60] = {};    P dots[60];    for (int i = 0; i < n; i += 1)      scanf("%lf%lf", &dots[i].x, &dots[i].y);    sort(dots, dots + n);    dp[0][1] = Dist(dots[0], dots[1]);    for (int i = 2; i < n; i += 1) {      dp[i - 1][i] = 1e30;      for (int j = 0; j < i - 1; j += 1) {        dp[i - 1][i] = min(dp[i - 1][i], dp[j][i - 1] + Dist(dots[j], dots[i]));        dp[j][i] = dp[j][i - 1] + Dist(dots[i - 1], dots[i]);      }    }    res = 1e30;    for (int i = 0; i < n - 1; i += 1)      res = min(res, dp[i][n - 1] + Dist(dots[i], dots[n - 1]));    printf("%.2lf\n", res);  }  return 0;}
1 0
原创粉丝点击