题目描述:———————————————————————————-
“从左到右再回来”不太方便思考,可以改成:两个人同时从最左点出发,沿着两条不同
的路径走,最后都走到最右点,且除了起点和终点外其余每个点恰好被一个人经过。这样,
就可以用d(i,j)表示第一个人走到i,第二个人走到j,还需要走多长的距离。
状态如何转移呢?仔细思考后会发现:好像很难保证两个人不会走到相同的点。例如,
计算状态d(i,j)时,能不能让i走到i+1呢?不知道,因为从状态里看不出来i+1有没有被j走
过。换句话说,状态定义得不好,导致转移困难。
下面修改一下:d(i,j)表示1~max(i,j)全部走过,且两个人的当前位置分别是i和j,还需
要走多长的距离。不难发现d(i,j)=d(j,i),因此从现在开始规定在状态中i>j。这样,不管是
哪个人,下一步只能走到i+ 1 , i+2,…这些点。可是,如果走到i+2,情况变成了“1~i和i+
2,但是i+1没走过”,无法表示成状态!怎么办?禁止这样的决策!也就是说,只允许其中
一个人走到i+1,而不能走到i+2, i+3,…。换句话说,状态d(i,j)只能转移到d(i+1,j)和d(i+
1,i) (4) 。
可是这样做产生了一个问题:上述“霸道”的规定是否可能导致漏解呢?不会。因为如果
第一个人直接走到了i+2,那么它再也无法走到i+1了,只能靠第二个人走到i+1。既然如
此,现在就让第二个人走到i+1,并不会丢失解。
边界是d(n-1,j)=dist(n-1,n)+dist(j,n),其中dist(a,b)表示点a和b之间的距离。因为根据
定义,所有点都走过了,两个人只需直接走到终点。所求结果是dist(1,2)+d(2,1),因为第一
步一定是某个人走到了第二个点,根据定义,这就是d(2,1)。
状态总数有O(n 2 )个,每个状态的决策只有两个,因此总时间复杂度为O(n 2 )。
#include<iostream>
#include<math.h>
#include<string.h>
#include<sstream>
#include<iomanip>
#define maxn 1000+10
using namespace std;
int n;
struct node
{
int x,y;
}loc[maxn];
double dp[maxn][maxn];
double dist[maxn][maxn];
void initial()
{
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
double detx=(double)(loc[i].x-loc[j].x);
double dety=(double)(loc[i].y-loc[j].y);
double temp=detx*detx+dety*dety;
dist[i][j]=dist[j][i]=sqrt(temp);
}
}
double fun(int i,int j)
{
double& ans=dp[i][j];
if(ans>0)return ans;
if(i==n-1)return ans=dist[i][n]+dist[j][n];
double p1=fun(i+1,j)+dist[i][i+1];
double p2=fun(i+1,i)+dist[j][i+1];
ans=min(p1,p2);
return ans;
}
void function(int n)
{
for(int i=1;i<=n;i++)
{
cin>>loc[i].x>>loc[i].y;
}
initial();
ostringstream oss;
oss<<setiosflags(ios::fixed)<<setprecision(2)<<fun(2,1)+dist[1][2]<<endl;
string s=oss.str();
cout<<s;
memset(dist,0,sizeof(dist));
memset(dp,0,sizeof(dp));
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
function(n);
}
}