题意:给定平面上n个点的坐标,设计一条路线,从最左边的点出发,走到最右边的点再返回,要求除了最左点和最右点之外每个点恰好经过一次,问最短路径。
典型的多决策问题,需要用DP解决,关键是每个点恰走一次,所以需要将使DP有序化,因此我们规定d[i][j]表示1~max(i,j)全走过,且当前两人位置是i和j,还需要走多长的距离。
因为这两个人是无所谓的,所以d[i][j] = d[j][i] 。因此不妨规定i > j,且规定d[i][j] 只能转移到d[i+1][j]和d[i][i+1],也就是说下一步只能到i+1。由于i > j,所以转移到d[i+1][j] 和d[i+1][i]
为什么这样是对的呢? 之前已经说了,d[i][j] = d[j][i] 所以转移到那两个状态的意思就是第一个人转移到下一步或者第二个人转移到下一步,因此这个记录过程其实和背包是一样的(选当前这个物品或者不选) , 也就是说之前的最优结果都被记录了,所以这样的递推做法是完美的。
细节参见代码:
#include<bits/stdc++.h> using namespace std; const int maxn = 1000 + 10; int n; double x[maxn],y[maxn],dist[maxn][maxn],d[maxn][maxn]; int main(){ while(~scanf("%d",&n)) { for(int i=1;i<=n;i++) scanf("%lf%lf",&x[i],&y[i]); //计算距离 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { dist[i][j] = sqrt((x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j])); } //dp for(int i=n-1;i>=1;i--) for(int j=1;j<i;j++) { if(i==n-1) d[i][j] = dist[n-1][n] + dist[j][n]; else d[i][j] = min(d[i+1][j] + dist[i][i+1],d[i+1][i] + dist[j][i+1]); } d[1][1] = d[2][1] + dist[1][2];//因为i > j ,所以循环中只记录到d[2][1] printf("%.2f\n",d[1][1]); } return 0; }