题意:
双调欧几里得旅行商问题。
思路:
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 ", solve()); 57 } 58 return 0; 59 }
总结:
假设一个最优选择,然后再基于该最优选择来定义问题,是动态规划的惯用手法。