刘汝佳给这道题的分析真的妙。太大佬了。
1.“从左到右再回来”不太方便思考,可以改成:两个人同时从最左点出发,沿着两条不同
的路径走,最后都走到最右点,且除了起点和终点外其余每个点恰好被一个人经过。 这样,
就可以用d(i,j)表示第一个人走到i,第二个人走到j,还需要走多长的距离。
这样换个方式思考就大大减少了复杂度!
这个状态转移:d(i,j)只能转移到d(i+1,j)和d(i+1,i)只适合递归记忆化搜索求解,因为我们不能事先知道
i+1的状态,我尝试过从后往前推,也是不可能的。(根据对称性也能想明白)。
如果写成循环递推的形式需要对状态转移方程进行修改,d(i, j)的得来应该分两种,j < i - 1 和j == i - 1时,
这两种递推需要分开写。d(i,j)= d(i - 1,j) + dist(i-1,i)和d(i,i-1)= min( d(i,i - 1),d(i - 1, j) + dist(j,i))
这两个式子看似互相独立,但是都影响着将来。
最后是dp的边界条件,初始肯定要有人走第一步,这个初始化一下。
第二个是i走到i后还要再让j走到最后一步。
#include
using namespace std;
#define M 1010
typedef pair P;
#define f first
#define s second
#define INF 100000000
P date[M];
double dp[M][M];
double ans;
//bool vis[M];
//distance
double dist(int i, int j)
{
double x = abs(date[i].f - date[j].f);
double y = abs(date[i].s - date[j].s);
return hypot(x, y);
}
int main()
{
int n;
while(scanf("%d", &n) != EOF)
{
for(int i = 1; i <= n; i++){
scanf("%d%d", &date[i].f, &date[i].s);
}
for(int i = 0; i <= n; i++){
for(int j = 0; j <= n; j++){
dp[i][j] = INF;
}
}
// memset(vis, 0, sizeof(vis));
//edge
dp[2][1] = dist(2, 1);
ans = INF;
for(int i = 3; i <= n; i++){
for(int j = 1; j < i - 1; j++){
dp[i][j] = dp[i - 1][j] + dist(i, i - 1);
dp[i][i - 1] = min(dp[i][i - 1], dp[i - 1][j] + dist(j, i));
}
}
//j‘s last step
for(int i = 1; i < n; i++)
ans = min(ans, dp[n][i] + dist(n, i));
printf("%.2lf\n", ans);
}
return 0;
}