vijos-p1014 2008.11.6

vijos-p1014 2008.11.6

动态规划 vijos-p1014--很强悍的dp Has comments

背景 Background 

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

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

描述 Description  

著名的NPC难题的简化版本

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

输入格式 Input Format 

第一行一个整数n

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

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

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

 输出格式 Output Format 

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

样例输入 Sample Input  

7

06

10

23

54

61

75

82

样例输出 Sample Output  

25.58

这个题就是赤裸裸的Bitonic Tour问题了。我的解决方案如下:

将一个人从最左端走到最右端,然后从最右端走到最左端等价成两个人同时从最左端不重复的走过中间的点并且到达最右端。问题的转化,模型的构建是多么的强悍!

我们不妨设这两个人为A和B,且总是假定走在前面的人是A。再设函数F(x1, x2)表示A走到x1的位置,B走到x2的位置,并且所有x1,x2之前的位置都被不重复的走过的最短距离之和。根据前面的假设我们可以知道 x1>=x2,且F(xn,xn)即为所得。

下面的过程就是如何推导出状态方程,我把它划分为三种情况:

1) 如果x1=x2,即A和B处在同一点,那么F(x1,x2) = F(x1, x1) = F(x1, x1 - 1) + dist(x1, x1 - 1)

2) 如果x1=x2+1,即B在A的紧邻的靠后一点,那么F(x1,x2) = F(x2, x') + dist(x1, x') (1<= x' <=x2)

{为什么要选择在x1=x2+1时为计算最优值得点呢?

因为,当x1=x2+1时,1---x1这些点,刚好被走完,所以在此时,需要找到一个在1—x1间,的最优值,故, for k:=1 to j-1 do

iff[i-1,k]+d[i,k]<min then min:=f[i-1,k]+d[i,k];

f[i,j]:=min;

}

3) 如果x1>x2+1,即B离A在后面一个距离的范围以上,那么F(x1, x2) = F(x1 - 1, x2) + dist(x1, x1 - 1)

其实这里不用考虑如果x1,x2走到中间同一个点,因为很容易得到那样不是最优的。

以上算法时间复杂度与空间复杂度均为O(n*n)

题解中的:

for i:=1 to n do

    begin

       for j:=1 to i-1 do

       if j=1 then

       f[i,j]:=f[i-1,1]+d[i-1,i]else

       if j=i-1 then

       begin

        min:=1e24;

        for k:=1 to j-1 do

        if f[i-1,k]+d[i,k]<min then min:=f[i-1,k]+d[i,k];

        f[i,j]:=min;

       end else

       f[i,j]:=f[i-1,j]+d[i-1,i];

    end;

我改进后的:

for i:=1 to n do

    begin

       for j:=1 to i-1 do

     if (j=i-1)and(j<>1)then

       begin

        min:=1e24;

        for k:=1 to j-1 do

        if f[i-1,k]+d[i,k]<min then min:=f[i-1,k]+d[i,k];

        f[i,j]:=min;

       end

    else

      f[i,j]:=f[i-1,j]+d[i-1,i];

    end;

program:
program vijosp1014;
const maxn=1000;
var
   x,y:array[1..maxn] of real;
   f,dist:array[0..maxn,0..maxn] of real;
   i,j,n,k:longint;
   min:real;
procedure qsort(p,q:longint);
var
   i,j:longint;
   temp,mid:real;
begin
     i:=p;j:=q;mid:=x[(i+j) div 2];
     repeat
           while x[i]>mid do inc(i);
           while x[j]<mid do dec(j);
           if i<=j
              then
              begin
                   temp:=x[i];
                   x[i]:=x[j];
                   x[j]:=temp;
                   temp:=y[i];
                   y[i]:=y[j];
                   y[j]:=temp;
                   inc(i);dec(j);
              end;
     until i>j;
     if i<q then qsort(i,q);
     if j>p then qsort(p,j);
end;
function d(x1,x2,y1,y2:real):real;
begin
     d:=sqrt(sqr(x1-x2)+sqr(y1-y2));
end;
begin
     readln(n);
     for i:=1 to n do readln(x[i],y[i]);
     qsort(1,n);
     for i:=1 to n do
         for j:=1 to n do
             dist[i,j]:=d(x[i],x[j],y[i],y[j]);
     for i:=1 to n do
         for j:=1 to i-1 do
              if (j=i-1)and(j<>1)

                        then
                        begin
                             min:=10000000000000;
                             for k:=1 to j-1 do
                                 if f[i-1,k]+dist[k,i]<min
                                    then min:=f[i-1,k]+dist[k,i];
                             f[i,j]:=min;
                        end
                else f[i,j]:=f[i-1,j]+dist[i-1,i];
     writeln(f[n,n-1]+dist[n-1,n]:0:2);{这里的结果也要记住}
分成两种情况:A在B前面,B在A前面。
end.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值