zoukankan      html  css  js  c++  java
  • P1523 旅行商简化版

    P1523 旅行商简化版

    题目背景

    欧几里德旅行商(Euclidean Traveling Salesman)问题也就是货郎担问题一直是困扰全世界数学家、计算机学家的著名问题。现有的算法都没有办法在确定型机器上在多项式时间内求出最优解,但是有办法在多项式时间内求出一个较优解。

    为了简化问题,而且保证能在多项式时间内求出最优解,J.L.Bentley提出了一种叫做bitonic tour的哈密尔顿环游。它的要求是任意两点(a,b)之间的相互到达的代价dist(a,b)=dist(b,a)且任意两点之间可以相互到达,并且环游的路线只能是从最西端单向到最东端,再单项返回最西端,并且是一个哈密尔顿回路。

    题目描述

    著名的NPC难题的简化版本

    现在笛卡尔平面上有n(n<=1000)个点,每个点的坐标为(x,y)(-2^31<x,y<2^31,且为整数),任意两点之间相互到达的代价为这两点的欧几里德距离,现要你编程求出最短bitonic tour。

    输入输出格式

    输入格式:

    第一行一个整数n

    接下来n行,每行两个整数x,y,表示某个点的坐标。

    输入中保证没有重复的两点,

    保证最西端和最东端都只有一个点。

    输出格式:

    一行,即最短回路的长度,保留2位小数。

    输入输出样例

    输入样例#1:
    7
    0 6
    1 0
    2 3
    5 4
    6 1
    7 5
    8 2
    
    输出样例#1:
    25.58
     

    分析:

    下面提供两种方法,刷表法和填表法
     
    1、刷表法
    代码是从前往后刷表

    【1】题意:旅行商问题,不过要求只能单向走,就是有n个地方,要求从西往东,到最东面的地方,在从东往西返回,经过每个点一次,求最短路径

    【2】分析:由于有了方向的限制,这题不再是NP难题,我们可以假设有两个人一起从西往东走,走过的点不能重复,这样就有f[ i ][ j ]表示第一个人走到i,第二个人走到j 的最短路径,要求i<j,且0到j的点都被经过了,这样很容易想到,j+1的点不是被第一个人走,就是被第二个人走,所以有转移方程f[ i ][ j+1]=min{ f[ i ] [ j ]+d[ j ] [ j +1] } f[ j ] [ j+1 ]=min{ f[ i ][ j ]+d[ i ][ j+1 ] },第一个转移方程很容易理解,第二个方程可以这么理解,两个人可以指前面一个人,和后面一个人,当后面的人走到前面,当然就对换过来了,不影响结果

    【3】最后,预处理f[ 0 ][ 1]还有扫描 一遍答案就行了,这题算是一类DP吧,思路挺有启发性的

     1 #include <cmath>
     2 #include <cstdio>
     3 #include <iostream>
     4 #include <cstring>
     5 #include <algorithm>
     6 using namespace std;
     7 const int mm=1111;
     8 struct data
     9 {
    10     double x,y;
    11 }g[mm];
    12 double d[mm][mm],f[mm][mm];
    13 int i,j,k,n;
    14 bool cmp(const data &a,const data &b)
    15 {
    16     return a.x<b.x;
    17 }
    18 double mdis(const data &a, const data &b)
    19 {
    20     return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    21 }
    22 int main()
    23 {
    24     scanf("%d",&n);
    25     for(i=0;i<n;++i)
    26         scanf("%lf%lf",&g[i].x,&g[i].y);
    27     sort(g,g+n,cmp);
    28     for(i=0;i<n;++i)
    29         for(j=i+1;j<n;++j)
    30         {
    31             d[i][j]=mdis(g[i],g[j]);
    32             f[i][j]=1e30;
    33         }
    34     f[0][1]=d[0][1];
    35     for(i=0;i<n;++i)
    36         for(j=i+1;j<n;++j)
    37         {
    38             f[i][j+1]=min(f[i][j+1],f[i][j]+d[j][j+1]);
    39             f[j][j+1]=min(f[j][j+1],f[i][j]+d[i][j+1]);
    40         }
    41     double ans=1e30;
    42     for(j=0;j<n-1;++j)
    43         ans=min(ans,f[j][n-1]+d[j][n-1]);
    44     printf("%.2lf
    ",ans);
    45     return 0;
    46 }

    2、填表法

    代码是从后往前填表

    遍历一整个图, 从开始位置走到末尾,再走回开始位置,并且不能重复;

    由于题目并没有对输入数据按X从小到大排序,所以先排序,然后预处理出每两个点之间的欧几里得距离。

    f(i,j)表示 从 1->max(i,j) 已经全部遍历切没有重复的最短路;

    那么可知F(i , j) = F(j , i); 于是就规定 i > j;

    如果 i = n - 1; 那么就强行将j跳到末尾位置;f(i, j) = dis(i, n) + dis(j, n);

    其余可知 f(i,j) = max ( f(i+1, j) + dis(i, i +1), f(i+1, i) + dis(j, i+1)) 原本应写成f(i, i+1), 但由于规定所以写成这样;

    最终状态为f(2, 1);

    接着就是naive的输出啦;

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cmath>
     5 #include<cstring>
     6 typedef long long Lovelive;
     7 using namespace std;
     8 
     9 const int maxn = 1000 + 10;
    10 int n;
    11 struct Node {
    12     double x, y;    
    13 }node[maxn];
    14 double dis[maxn][maxn], f[maxn][maxn];
    15 
    16 bool cmp(Node a, Node b) {
    17     return a.x < b.x;    
    18 }
    19 
    20 int main() {
    21     scanf("%d",&n);    
    22     for(int i = 1; i <= n; i++) scanf("%lf%lf",&node[i].x,&node[i].y);
    23     sort(node+1, node+n+1, cmp);
    24     for(int i = 1; i <= n; i++)
    25         for(int j = 1; j <= n; j++)
    26             dis[i][j] = sqrt((node[i].x-node[j].x)*(node[i].x-node[j].x) + (node[i].y-node[j].y)*(node[i].y-node[j].y));
    27 
    28     for(int i = n-1; i >= 2; i--)
    29         for(int j = 1; j < i; j++)
    30             if(i == n-1) f[i][j] = dis[i][n] + dis[j][n];
    31             else f[i][j] = min (f[i+1][j] + dis[i][i+1], f[i+1][i] + dis[j][i+1]);
    32     printf("%.2lf",f[2][1] + dis[1][2]);
    33     return 0;
    34 }
     
     
     
     
     
  • 相关阅读:
    灵活读取Configuration文件
    Web页面访问权限
    母版页(MasterPage),你真得了解了吗?
    基于对象和面向对象
    [翻译]Silverlight 3中的tooltip
    2009年自我总结
    W3C CSS Validator 更喜欢CSS文件以一个class开头而不是注释?
    【翻译】读取文本文件(txt、csv、log、tab、fixed length)(上)
    1z0062 题库解析2
    1z0062 题库解析3
  • 原文地址:https://www.cnblogs.com/Renyi-Fan/p/7617343.html
Copyright © 2011-2022 走看看