题意:

双调欧几里得旅行商问题。

思路:

dp。定义dp[i][j](i <= j)为从点j从右向左严格按照x坐标递减顺序走到点1,之后再从点1从左向右严格按照x坐标递增的顺序走到点i,并且在此过程中经过且仅经过1到j之间所有的点1次。则

dp[1][2] = dis[1][2];

dp[i][j] = dp[i][j - 1] + dis[j - 1][j]; (i < j - 1)

dp[i][j] = min(dp[k][j - 1] + dis[k][j]). (1 <= k < j - 1, i == j - 1)

最终,dp[n][n] = dp[n - 1][n] + dis[n - 1][n].

实现:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cmath>
 5 using namespace std;
 6 
 7 const int MAXN = 50, INF = 0x3f3f3f3f;
 8 double dp[MAXN + 5][MAXN + 5], dis[MAXN + 5][MAXN + 5];
 9 int n, x[MAXN + 5], y[MAXN + 5];
10 
11 int square(int x)
12 {
13     return x * x;
14 }
15 
16 void init()
17 {
18     for (int i = 1; i <= n; i++)
19     {
20         for (int j = i + 1; j <= n; j++)
21         {
22             dis[i][j] = sqrt(square(x[i] - x[j]) + square(y[i] - y[j]));
23         }
24     }
25 }
26 
27 double solve()
28 {
29     dp[1][2] = dis[1][2];
30     for (int j = 3; j <= n; j++)
31     {
32         // i < j - 1
33         for (int i = 1; i <= j - 2; i++)
34         {
35             dp[i][j] = dp[i][j - 1] + dis[j - 1][j];
36         }
37         // i == j - 1
38         dp[j - 1][j] = INF;
39         for (int k = 1; k <= j - 2; k++)
40         {
41             dp[j - 1][j] = min(dp[j - 1][j], dp[k][j - 1] + dis[k][j]);
42         }
43     }
44     return dp[n][n] = dp[n - 1][n] + dis[n - 1][n];
45 }
46 
47 int main()
48 {
49     while (cin >> n)
50     {
51         for (int i = 1; i <= n; i++)
52         {
53             cin >> x[i] >> y[i];
54         }
55         init();
56         printf("%.2f\n", solve());
57     }
58     return 0;
59 }

总结:

假设一个最优选择,然后再基于该最优选择来定义问题,是动态规划的惯用手法。

 

相关文章: