The shortest path
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Before you reach the rightmost point Pn, you can only visit the points those have the bigger x-coordinate value. For example, you are at Pi now, then you can only visit Pj(j > i). When you reach Pn, the rule is changed, from now on you can only visit the points those have the smaller x-coordinate value than the point you are in now, for example, you are at Pi now, then you can only visit Pj(j < i). And in the end you back to P1 and the tour is over.
You should visit all points in this tour and you can visit every point only once.
3 1 1 2 3 3 1
6.47 Hint: The way 1 - 3 - 2 - 1 makes the shortest path.解题思路:这道题目是一道双调旅行商问题:欧几里德旅行商问题是对平面上给定的n个点的确定一条连接各点的最短闭合旅程的问题。图a给出了一个7个点问题的解。这个问题的一般形式是NP完全的,故其解需要多余多项式的时间。
J.L.Bentley建议通过只考虑双调旅程来简化问题,这种旅程即为从最左点开始,严格地从左到右直至最右点,然后严格地从右到左直至出发点。图b显示了同样的7个点问题的最短双调路线。在这种情况下,多项式时间的算法是可能的。
描述一个确定最优双调路线的O(n^2)时间的算法。可以假设任何两点的x坐标都不相同。
一个人从最左点开始,严格地从左到右直至最右点,然后从右到左直至出发点,可以等价为两个人同时从最左点,严格地从左到右经历不同路径到达最右点。 假设这两个人为A和B,且A总是走在B后面。设Pij表示A走到pi、B走到pj时两人所经过的最短双调路径,根据假设,可得i<=j。又设dp[i, j]表示最短双调路径Pij的长度,dis[i, j]表示点pi到点pj的直线距离,则: dp[1, 2]=dis[1, 2] 当i=j时,即A和B处于同一点,dp[i, j]=dp[i, i]=dp[i-1, i]+dis[i-1, i] 当i=j-1时,即A在B紧邻的靠后一点,dp[i, j]=dp[j-1, j]=min(1<=k<j-1){dp[k, j-1]+dis[k, j]} 当i<j-1时,即A在B后且相隔多个点,dp[i, j]=dp[i, j-1]+dis[j-1, j]
由几何学知识可得,如果中间路径A和B经历了同一点,则这条路径肯定不是最短路径,故i=j的情况只可能用来计算b[n, n]=b[n-1, n]+d[n-1, n]。
这里最难理解的是当i=j-1时,按照定义的dp来讲,dp[k][j-1]应该是A走到k,B走到j-1,那这样怎么会走到i的呢?其实这里A和B的位置不是固定的,这里实际上是把之前k给了A,j-1给了B,因为A与B两者只差一个位置,如果只是从j-1转移到j的话,那么就会走重复的点,肯定不能这样,由于只要知道A与B的路径之和就行了,所以A与B的位置并不会固定死了。这个状态转移确实要多想想。
AC:#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; const int inf = 0x3f3f3f3f; int n; struct node { double x,y; }pos[205]; double dp[205][205]; double distance(int i,int j) { double tmp; tmp = (pos[i].x - pos[j].x)*(pos[i].x - pos[j].x) + (pos[i].y - pos[j].y)*(pos[i].y - pos[j].y); return sqrt(tmp); } int main() { while(scanf("%d",&n)!=EOF) { for(int i = 1; i <= n; i++) scanf("%lf %lf",&pos[i].x,&pos[i].y); dp[1][2] = distance(1,2); for(int j = 3; j <= n; j++) { // i < j-1 dp[i][j] = dp[i][j-1] + distance(j-1,j); for(int i = 1; i < j - 1; i++) { dp[i][j] = dp[i][j-1] + distance(j-1,j); } dp[j-1][j] = inf; //i = j-1 dp[i][j] = min{dp[k][i]+distance(k,j)} for(int i = 1; i < j - 1; i++) { double tmp = dp[i][j-1] + distance(i,j); if(tmp < dp[j-1][j]) dp[j-1][j] = tmp; } } dp[n][n] = dp[n-1][n] + distance(n-1,n); printf("%.2lf\n",dp[n][n]); } return 0; }