Uva--1347(动规,深入分析,递推)
2014-08-26 13:45:28
Tour
John Doe, a skilled pilot, enjoys traveling. While on vacation, he rents a small plane and starts visiting beautiful places. To save money, John must determine the shortest closed tour that connects his destinations. Each destination is represented by a point in the plane pi = < xi, yi > <tex2html_verbatim_mark>. John uses the following strategy: he starts from the leftmost point, then he goes strictly left to right to the rightmost point, and then he goes strictly right back to the starting point. It is known that the points have distinct x <tex2html_verbatim_mark>-coordinates.
Write a program that, given a set of n <tex2html_verbatim_mark>points in the plane, computes the shortest closed tour that connects the points according to John's strategy.
Input
The program input is from a text file. Each data set in the file stands for a particular set of points. For each set of points the data set contains the number of points, and the point coordinates in ascending order of the x<tex2html_verbatim_mark>coordinate. White spaces can occur freely in input. The input data are correct.
Output
For each set of data, your program should print the result to the standard output from the beginning of a line. The tour length, a floating-point number with two fractional digits, represents the result.
Note: An input/output sample is in the table below. Here there are two data sets. The first one contains 3 points specified by their x <tex2html_verbatim_mark>and y <tex2html_verbatim_mark>coordinates. The second point, for example, has the x <tex2html_verbatim_mark>coordinate 2, and the y<tex2html_verbatim_mark>coordinate 3. The result for each data set is the tour length, (6.47 for the first data set in the given example).
Sample Input
3 1 1 2 3 3 1 4 1 1 2 3 3 1 4 2
Sample Output
6.47 7.89
思路:这样的题目时间分配差不多8分想2分敲。。。。这道第二版小白书的例题在书里讲的已经比较透彻了,这里来归纳一下分析的过程(如有分析有误,还请大牛指正!)
(1)如果老老实实地从左到右再回来考虑比较麻烦,应转化成两个人从起点出发,在终点会和的问题。
(2)如果用dp[i][j]记录第一个人走到i,第二个人走到j,还需要多少距离的话(就是以(i,j)为起点的定义方法),但是这样会遇到一个问题:dp[i][j]转移到下一个状态时,比如考虑i走到i+1时,我们并不知道第i+1个点是否已经走过,也就是说记录的状态太狭窄(too simple),导致状态转移困难。
(3)于是我们想办法把一个点是否已经走过记录下来,用dp[i][j]来表示1 - max(i,j)这些点全部走过,而且两个人的当前位置是i、j。
(4)不难发现dp[i][j] == dp[j][i],于是不妨令i > j,于是dp[i][j]能转移到的状态就只有dp[i + 1][j]、dp[i + 1][i];
(思考:这里为什么能把dp[i][j]里的i、j定义为i > j呢?这样定义了之后其实就无法得知当前在第i个点的人是第一个人还是第二个人!但是巧妙的是,这题恰恰不用这样考虑,因为这题只需要求出最小长度,而不用求出达到最小长度的方案数。如果要计数的话dp[i][j]就得定义为1 - max(i,j)这些点走过,而且第一个人在i,第二个人在j 这种更严格的定义,所以(4)里面的定义方法实际上已经是优化过的定义方法,算法复杂度是这样定义的1 / 2)
(5)注意边界:dp[n - 1][j] = dist[n - 1][n] + dist[j][n]。 (dist[i][j]表示点i到点j的距离)
O(n^2)的算法:
1 /************************************************************************* 2 > File Name: 1347.cpp 3 > Author: Nature 4 > Mail: 564374850@qq.com 5 > Created Time: Tue 26 Aug 2014 11:00:57 AM CST 6 ************************************************************************/ 7 8 #include <cstdio> 9 #include <cstring> 10 #include <cstdlib> 11 #include <cmath> 12 #include <iostream> 13 #include <algorithm> 14 using namespace std; 15 const int INF = 1 << 30; 16 17 int n; 18 double x[1005]; 19 double y[1005]; 20 double dp[1005][1005]; 21 double dist[1005][1005]; 22 23 double Cal(int a,int b){ 24 return sqrt((x[b] - x[a]) * (x[b] - x[a]) + (y[b] - y[a]) * (y[b] - y[a])); 25 } 26 27 void Init(){ 28 for(int i = 1; i < n; ++i) 29 for(int j = i + 1; j <= n; ++j) 30 dist[i][j] = Cal(i,j); 31 } 32 33 int main(){ 34 while(scanf("%d",&n) == 1){ 35 for(int i = 1; i <= n; ++i) 36 scanf("%lf%lf",&x[i],&y[i]); 37 Init(); 38 for(int i = 1; i < n - 1; ++i) 39 dp[n - 1][i] = dist[n - 1][n] + dist[i][n]; 40 for(int i = n - 2; i >= 2; --i){ 41 for(int j = 1; j < i; ++j){ 42 dp[i][j] = min(dp[i + 1][j] + dist[i][i + 1],dp[i + 1][i] + dist[j][i + 1]); 43 } 44 } 45 printf("%.2lf\n",dp[2][1] + dist[1][2]); 46 } 47 return 0; 48 }

浙公网安备 33010602011771号