天天看點

3510. 【NOIP2013模拟11.5B組】最短路徑(path)

Description

平面内給出 n 個點,記橫坐标最小的點為 A,最大的點為 B,現在Zxd想要知道在每個點經過一次(A 點兩次)的情況下從 A 走到 B,再回到 A 的最短路徑。但他是個強迫症患者,他有許多奇奇怪怪的要求與限制條件:

1.  從 A 走到 B 時,隻能由橫坐标小的點走到大的點。

2.  由 B 回到 A 時,隻能由橫坐标大的點走到小的點。

3.  有兩個特殊點 b1 和 b2, b1 在 0 到 n-1 的路上,b2 在 n-1 到 0 的路上。

請你幫他解決這個問題助他治療吧!  

Input

第一行三個整數 n,b1,b2,( 0 < b1,b2 < n-1 且 b1 <> b2)。n 表示點數,從 0 到 n-1 編号,b1 和 b2 為兩個特殊點的編号。

以下 n 行,每行兩個整數 x、y 表示該點的坐标(0 <= x,y <= 2000),從 0 号點順序

給出。Doctor Gao為了友善他的治療,保證所有點橫坐标不同,并且已經将給出的點按 x 增序排好了。  

Output

僅一行,輸出最短路徑長度(精确到小數點後面 2 位)  

Solutions

考慮到每個點隻能走一次,且從終點往回走和從起點再走一遍到終點沒有差別,是以這道題可以轉化為求兩條不相交路徑和的最小值。

    于是考慮用動态規劃求解。

    用F[i][j]表示第一個點走到i,第二個點(回去的那個點)走到j的最優值。

    為了保證更新時不會更新出F[i][i](即一個點走了兩次),而且每個點都會在路徑上,我們每次用F[i][j]去更新點max(i,j)+1,是以轉移方程為:

    F[0][0]=0; k=max(i,j)+1,

    F[k][j]=max(F[k][j],F[i][j]+Dis(i,k));

    F[i][k]=max(F[i][k],F[i][j]+Dis(j,k));

    Dis(i,j)為從i直接走到j點的距離.

對于兩個特殊點和max(i,j)=N的情況特判處理即可。

期望得分:100(本代碼為此做法)

    同時上面的DP也可以用記憶化搜尋實作,對于abs(x-y)>1的情況,說明目前情況隻能從max(x,y)-1轉移過來,當abs(x-y)=1時,則能從1~min(x,y)中的任意一點轉移過來,于是用記憶化搜尋完成上面的步驟,加上适當剪枝即可。

期望得分:60~100分

代碼

1 var
 2   n,b1,b2:longint;
 3   x,y:array [0..1001] of longint;
 4   f,b:array [0..1001,0..1001] of real;
 5 procedure init;
 6 var
 7   i:longint;
 8 begin
 9   readln(n,b1,b2);
10   b1:=b1+1; b2:=b2+1;
11   for i:=1 to n do
12     readln(x[i],y[i]);
13 end;
14 
15 function min(o,p:real):real;
16 begin
17   if o<p then exit(o);
18   exit(p);
19 end;
20 
21 function max(o,p:longint):longint;
22 begin
23   if o>p then exit(o);
24   exit(p);
25 end;
26 
27 procedure main;
28 var
29   i,j,k:longint;
30 begin
31   for i:=1 to n-1 do
32     for j:=i+1 to n do
33       begin
34         b[i,j]:=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
35         b[j,i]:=b[j,i];
36       end;
37   for i:=0 to n do
38     for j:=0 to n do
39       f[i,j]:=maxlongint div 3;
40   f[1,1]:=0;
41   for i:=1 to n do
42     for j:=1 to n do
43       if (i<>j) or (i=1) then
44         begin
45           k:=max(i,j)+1;
46           if k=n+1 then
47             begin
48               if i<>n then f[n,n]:=min(f[n,n],f[i,j]+b[i,n]);
49               if j<>n then f[n,n]:=min(f[n,n],f[i,j]+b[j,n]);
50               continue;
51             end;
52           if k<>b1 then f[i,k]:=min(f[i,k],f[i,j]+b[j,k]);
53           if k<>b2 then f[k,j]:=min(f[k,j],f[i,j]+b[i,k]);
54         end;
55   writeln(f[n,n]:0:2);
56 end;
57 
58 begin
59   init;
60   main;
61 end.