zoukankan      html  css  js  c++  java
  • 【小专题】哈密顿回路

    哈密顿回路来历:

    天文学家哈密顿(William Rowan Hamilton) 提出,在一个有多个城市的地图网络中,寻找一条从 给定的起点 到 给定的终点 沿途恰好经过所有其他城市一次的路径。

    这个问题和著名的七桥问题的不同之处在于,七桥问题过桥只需要确定起点,而不用确定终点。

    哈密顿回路问题已被证明是NP问题,具有这样性质的问题,很难找到一个有效的算法。实际上对于某些顶点数不到100的网络,利用现有最好的算法和计算机也需要比较荒唐的时间(比如几百年)才能确定其是否存在一条这样的路径。

    今天,我们就来看看这类问题是什么样的呢?(“这类问题”说明不一定只是问你是否存在这样一条路径)

    由于这是个NP问题,因此我目前的菜鸡水平只能做简化版的。

    luogu1523.旅行商简化版

    题目背景

    欧几里德旅行商(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

    这就是哈密顿问题的简化版。它把所有点放在了坐标轴上,并且规定了必须从最左端到最右端再回到最多段(就是规定了方向)。
    由于题目中没有给每个点的坐标排序,我们可以先以横坐标x为第一关键字,纵坐标y为第二关键字从小到大排序。
    这些点排好序之后,由于规定了方向,问题就转化成了求2条从最左上端(第一个点)到最右下端(最后一个点)的不相交的路径,且这两条路径经过所有点。把这两条路径都看做向右走的即可。
    由于规定了方向,如果两条路径分别到达了第i、j个点,那么说明1到max(i,j)号点一定都经过了(因为只能向右走而不能折返,为了经过所有的点,左边的点必须都经过了才能到达本列)。
    总结做法:
      f[i][j]表示第一条路径走到第i个点,第二条路径走到第j个点的最短路径,要求i<j(这样max(i,j)就是j,能保证第二条路径一定走在前面),且第1到j个的点都被经过了。这样很容易想到,第j+1个点不是被第一个人走,就是被第二个人走,所以有转移方程
      $f[i][j+1] = min{ f[i][j+1], f[i][j]+dis[j][j+1] }$
      $f[j][j+1] = min{ f[i][j+1], f[i][j]+dis[i][j+1] }$
    第一个方程是让第二条路径走到第j+1个点,没啥问题。
    第二个方程是让第一条路径走到第j+1个点,但不好理解,且这么走后是不是违反了 第一条路径到达的点i < 第二条路径到达的点j 的原则?但我没有明确指定哪条路径是第一条,哪条路径是第二条呀!因此当第一条路径跑到前面(即第j+1个点)时,我们可以把两条路径“交换”一下,而这样做不会影响状态,相当于第一条路径成了第二条路径,走到了第j个点;而第二条路径成了第一条路径,走到了第j+1个点。如此即可确保第二条路径仍在第一条路径前面了!

     1 #include<bits/stdc++.h>
     2 #define maxn 1001
     3 #define inf 1e30
     4 using namespace std;
     5 struct data
     6 {
     7     double x,y;
     8 }g[maxn];
     9 double d[maxn][maxn],f[maxn][maxn];
    10 int i,j,k,n;
    11 bool cmp(const data &a,const data &b)
    12 {
    13     if(a.x==b.x) return a.y<b.y;
    14     return a.x<b.x;
    15 }
    16 double mdis(const data &a, const data &b)
    17 {
    18     return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    19 }
    20 int main()
    21 {
    22     scanf("%d",&n);
    23     for(i=1;i<=n;++i) scanf("%lf%lf",&g[i].x,&g[i].y);
    24     sort(g+1,g+n+1,cmp);//按横坐标x从小到大排序 
    25     for(i=1;i<=n;++i)
    26         for(j=i+1;j<=n;++j)
    27         {
    28             d[i][j]=mdis(g[i],g[j]); //求任意两点的距离 
    29             f[i][j]=inf;
    30         }
    31     f[1][2]=d[1][2];
    32     for(i=1;i<n;++i)
    33         for(j=i+1;j<=n;++j)
    34         {
    35             f[i][j+1]=min(f[i][j+1],f[i][j]+d[j][j+1]);
    36             f[j][j+1]=min(f[j][j+1],f[i][j]+d[i][j+1]);
    37         }
    38     printf("%.2lf
    ",f[n-1][n]+d[n-1][n]);
    39     return 0;
    40 }
    View Code

    旅行商问题

    题目描述

    给定一个n个顶点组成的带权有向图的距离矩阵d(i,j)(INF表示没有边)。要求从顶点0出发,经过每个顶点恰好一次后再回到顶点0.问所经过的边的总权重的最小值是多少?

    输入样例#1:

    7
    0 6
    1 0
    2 3
    5 4
    6 1
    7 5
    8 2

    输出样例#1:

    25.58

  • 相关阅读:
    NET 获取实例所表示的日期是星期几
    NET npoi 保存文件
    快速排序
    JAVA poi 合并单元格
    JAVA poi 帮助类
    JAVA 字符串编码转换
    NET npoi 合并单元值处理
    NET npoi帮助类
    Task的暂停,继续,取消
    .net ref与out之间区别
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/Hamilton.html
Copyright © 2011-2022 走看看