黑书1.5.2 例题7 青蛙的烦恼
没找到原题,这个题和原题差不多吧,就是没规定从1开始,可以从任意点开始,难了一点点吧。首先可以证明:最短路径一定没有交叉边,否则还可以更短。这就要求我们i点,只可能跳到i+1,i+len-1两点。这样就有了最优子结构,用dp[0][i][len]表示从i开始,遍历{i..i+len-1}中的顶点一次且仅一次的最短距离。dp[1][i][len]表示从i+len-1开始,遍历{i..i+len-1}中的顶点一次且仅一次的最短距离。则状态转移方程为:
dp[0][i][len]=min(dp[0][i+1][len−1]+dis[i][i+1],dp[1][i+1][len−1]+dis[i][i+len−1]);
dp[1][i][len]=min(dp[1][i][len−1]+dis[i+len−1][i+len−2],dp[0][i][len−1]+dis[i][i+len−1]);
答案为
min{dp[0][i][n]|1≤i≤n}
复杂度是
O(n2)
的
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
using namespace std;
#define N 205
int n;
double x[N],y[N],dis[N][N],dp[2][N][N];//0--i,1--i+len-1
int main(){
// freopen("a.in","r",stdin);
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)
dis[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
for(int i=1;i<=n;++i) dp[1][i][1]=dp[0][i][1]=0;
for(int len=2;len<=n;++len)
for(int i=1;i<=n;++i){
dp[0][i][len]=min(dp[0][(i+1-1)%n+1][len-1]+dis[i][(i+1-1)%n+1],dp[1][(i+1-1)%n+1][len-1]+dis[i][(i+len-1-1)%n+1]);
dp[1][i][len]=min(dp[1][i][len-1]+dis[(i+len-1-1)%n+1][(i+len-2-1)%n+1],dp[0][i][len-1]+dis[i][(i+len-1-1)%n+1]);
}
double ans=1000000000.0;
for(int i=1;i<=n;++i) if(dp[0][i][n]<ans) ans=dp[0][i][n];
printf("%.3lf\n",ans);
return 0;
}