本题难在设置状态表示。
题目要求先从左到右,再从右到左沿x轴方向经历坐标系上的点(1~n点),可以考虑成两条路径,分别从最左到最右,分别经历不同点且不能重合(除了起点和终点)。可以想到用$d(i,j)$表示一条路走到了$i$,一条路走到了$j$。因为两条路加起来要cover路径上所有点,所以$d(i,j)$可以表示$max(i,j)$和之前的点已经全被走过,还需走多远才能满足条件。因为两条路没有优劣,所以$d(i,j)==d(j,i)$,为了方便操作,可以规定要求$i>j$。可以用例子来理解:第一个人走到2,第二个人就只能停在1了,第一个人走到3,第一个人可以走到1或者2,第一个人走到4,第一个人可以走到1,2,3,始终让$i>j$可以满足结构的清晰。也就是说,$i$决定自己已经走到了最远的地方,而此时$j$停留在哪个地方,来保证$i$及以前的地方全部都被走过(可以理解为$j$给$i$擦屁股)。当$i$到达n时,$j$可以在路上$(1 ~ n-1)$中任何点,因为它走的是$i$走剩的点,所以$d(n,j)=0$。而状态方程可以写为:$d(i,j)=min[d(i+1,j)+dist(i,i+1),d(i+1,i)+dis(j,i+1)]$,其中$dist(a,b)$表示$a,b$之间的距离。
边界条件:当$i$到达n时,$j$可以在路上$(1 ~ n-1)$中任何点,因为它走的是$i$走剩的点,所以$d(n,j)=0$。(这个边界条件是错误的,因为两条路径最终都要到最右端),我们可以理解$d(n,j)=dist(j,n)$,和上面的状态方程结合起来,可以得到$d(n-1,j)=dist(n-1,n)+dist(j,n)$(刘汝佳)(我觉得还是分开来看来处理边界条件比较方便)。起始的时候,因为规定了$i>j$,所以只能写$$d(2,1)$,然后加上1走到2的距离就是总距离了。
代码如下:
1 // 2 // main.cpp 3 // Tour 4 // 5 // Created by Yanbin GONG on 12/3/2018. 6 // Copyright © 2018 Yanbin GONG. All rights reserved. 7 // 8 9 #include <iostream> 10 #include <stdio.h> 11 #include <string> 12 #include <string.h> 13 #include <algorithm> 14 #include <cmath> 15 16 using namespace std; 17 18 struct coord{ 19 int x,y; 20 }; 21 22 int n;//number of points 23 coord nodes[105]; //假设最多100个点,UVa官网貌似没给大小 24 double d[105][105]; 25 bool visited[105][105]; 26 double ans; 27 28 29 double dis(int a, int b){ 30 return sqrt((nodes[a].x-nodes[b].x)*(nodes[a].x-nodes[b].x)+(nodes[a].y-nodes[b].y)*(nodes[a].y-nodes[b].y)); 31 } 32 33 double dp(int a, int b){ 34 if(visited[a][b]==true){ 35 return d[a][b]; 36 } 37 visited[a][b] = true; 38 if(a==n){ 39 d[a][b] = dis(b,n); 40 return d[a][b];//终点条件 41 } 42 d[a][b] = min(dp(a+1,b)+dis(a,a+1),dp(a+1,a)+dis(b,a+1)); 43 return d[a][b]; 44 } 45 46 int main(){ 47 //原题用freopen,读取文件 48 while(scanf("%d",&n)!=EOF){ 49 memset(d,0x3f,sizeof(d)); 50 memset(visited,0,sizeof(visited)); 51 for(int i=1;i<=n;i++){ 52 cin>>nodes[i].x>>nodes[i].y; 53 } 54 dp(2,1); 55 ans = d[2][1] + dis(1,2); 56 printf("%.2lf ",ans); 57 } 58 59 return 0; 60 }
相同问题:OJ 440