J.L. Bentley 建议通过只考虑双调旅程(bitonic tour)来简化TSP问题。,这种旅程即为从最左点开始,严格地从左到右直至最右点,然后严格地从右到左直至出发点。
双线性DP。
将一个人从最左端走到最右端,然后从最右端走到最左端等价成两个人同时从最左端不重复的走过中间的点并且到最右端。
我们不妨设这两个人为A和B,且总是假定走在前面的人是A。
再设函数dp(i, j)表示A走到i的位置,B走到j的位置,并且所有i,j之前的位置都被不重复的走过的最短距离之和。
由此得到递推公式:
dp[i+1][i] = min(dp[i+1][i], dp[i][j] + distance(j, i+1));
dp[i+1][j] = min(dp[i+1][j], dp[i][j] + distance(i, i+1));
由于最右节点必然是终点,所以走的快的人必然到达了n点,最终的距离就是枚举i,dp[n][i] + distance(i, n)中最小的。
http://poj.org/problem?id=2677
j < i , j 跳到i+1 。
j < i , i往右跳到 i+1。
const int Max_N = 208 ; double dist[Max_N][Max_N] ; double dp[Max_N][Max_N] ; struct Point{ double x ; double y ; void read(){ scanf("%lf%lf" ,&x ,&y) ; } double Dist(Point &A){ return sqrt((x-A.x) * (x-A.x) + (y-A.y)*(y-A.y)) ; } }p[Max_N] ; int N ; double DP(){ int i , j ; double ans = 1e10 ; for(i = 2 ; i <= N ; i++){ for(j = i ; j <= N ; j++){ dist[i][j] = dist[j][i] = p[i].Dist(p[j]) ; dp[i][j] = dp[j][i] = 1e10 ; } } dp[2][1] = dist[2][1] ; for(i = 1 ; i < N ; i++){ for(j = 1 ; j < i ; j++){ dp[i+1][i] = min(dp[i+1][i] , dp[i][j] + dist[j][i+1]) ; dp[i+1][j] = min(dp[i+1][j] , dp[i][j] + dist[i][i+1]) ; } } for(i = 1 ; i < N ; i++) ans = min(ans , dp[N][i] + dist[i][N]) ; return ans ; } int main(){ int i ; while(cin>>N){ for(i = 1 ; i <= N ; i++) p[i].read() ; printf("%.2lf\n" ,DP()) ; } return 0 ; }
ZOJ 2096
这个题目是Bitonic Tour的一个变种,将欧几里德平面的点变成了坐标轴上的点,因此距离函数dist由笛卡尔距离变成了坐标轴距离。
另外规则上也有一些变动,只需从左到走到最右,然后反过来从最右把左边所有没走过的走一下。
言下之意就是说起点不是固定的。我们不妨假设点的标号为1~n,这里我们虚拟一个起点0,
dist[0][i] = 0 ;
const int Max_N = 108 ; int x[Max_N] ; int dist[Max_N][Max_N] ; int dp[Max_N][Max_N] ; int N ; inline int Abs(int x){ return x > 0 ? x : -x ; } int DP(){ int i , j ; int ans = 1<<30 ; for(i = 2 ; i <= N ; i++){ for(j = 2 ; j <= N ; j++){ dist[i][j] = dist[j][i] = Abs(x[i] - x[j]) ; dp[i][j] = dp[j][i] = 1<<30 ; } } for(i = 2 ; i <= N ; i++){ dist[1][i] = dist[i][1] = 0 ; dp[1][i] = dp[i][1] = 1<<30 ; } dp[1][1] = 0 ; dp[2][1] = dist[2][1] ; for(i = 2 ; i < N ; i++){ for(j = 1 ; j < i ; j++){ dp[i+1][i] = min(dp[i+1][i] , dp[i][j] + dist[j][i+1]) ; dp[i+1][j] = min(dp[i+1][j] , dp[i][j] + dist[i][i+1]) ; } } for(i = 1 ; i < N ; i++) ans = min(ans , dp[N][i] + dist[i][N]) ; return ans ; } int main(){ int i ; while(cin>>N && N){ N++ ; for(i = 2 ; i <= N ; i++) scanf("%d" ,&x[i]) ; printf("%d\n" ,DP()) ; } return 0 ; }
2014年百度之星程序设计大赛 - 资格赛
const int maxn = 1008 ; int x[maxn] , y[maxn] ; int dp[maxn][maxn] ; int N ; int dist[maxn][maxn] ; int getdist(int a , int b){ int t = (y[a] >= y[b])? (y[a] - y[b]) : (y[b] - y[a]) ; return min(t , 360-t) ; } int DP(){ int i , j , ans = 100000000 ; for(i = 1 ; i <= N ; i++){ for(j = i ; j <= N ; j++){ dist[i][j] = dist[j][i] = getdist(i , j) ; dp[i][j] = dp[j][i] = 100000000 ; } } dp[2][1] = dist[2][1] ; for(i = 2 ; i < N ; i++){ for(j = 1 ; j < i ; j++){ dp[i+1][i] = min(dp[i+1][i] , dp[i][j] + dist[j][i+1]) ; dp[i+1][j] = min(dp[i+1][j] , dp[i][j] + dist[i][i+1]) ; } } for(i = 1 ; i < N ; i++) ans = min(ans , dp[N][i] + dist[i][N]) ; return ans ; } int main(){ int t , i , row , col , n , s ; cin>>t ; while(t--){ scanf("%d" ,&n) ; N = n+1 ; x[1] = y[1] = 0 ; for(i = 2 ; i <= N ; i++){ scanf("%d%d" ,&x[i] , &y[i]) ; } s = x[N]*2*400 + 10*n ; s += DP() ; cout<< s << endl ; } return 0 ; }