紫书第九章-----动态规划初步(例题9-3 Tour UVA - 1347)

本文参考刘汝佳《算法竞赛入门经典》(第2版)
* 动态规划的核心是状态和状态转移方程*

【递推法】

/*
【思考题目】
(1)注意一点,去必须从左到右,回必须从右到左,那么去路确定了,返程路径随之确定。
(2)题目没有给定数据范围
状态确定不了,笔者无力解决此问题……
【解决题目】
参考刘汝佳《算法竞赛入门经典》(第2版)
结合对问题的思考,我们知道从左往右走过之后,返程路径随之确定,既然如此,我们可以等价为两个人同时从起点出发,
两个人途径所有点之后到达终点,且两个人除了起点和终点外,不能走重复的点。那么由此我们开始定义了状态……………………

先是定义状态d(i,j)表示第一个人走到i,第二个人走到j,还需要走多长距离。但这样规定状态后,能不能让从i转移到i+1呢?
未必可以,因为可能第二个人已经把i+1走过了,这就麻烦了,无法确定能否往下一步转移!!!

重新定义状态d(i,j)表示1~max(i,j)所有点都走过了,两个人当前位置分别是i,j。显然d(i,j)=d(j,i),现在强制i>j。现在两
人下一步只能走i+1,i+2,...这些点。可是如果走到i+2,i+1就没人走了!因此状态只能从d(i,j)转移到d(i+1,j)和d(i,i+1),
由于我们规定了i>j,那么我们把d(i,i+1)写成d(i+1,i)。
那么我们有:
状态:d(i,j):1~max(i,j)所有点都走过了,两个人当前位置分别是i,j
状态转移方程:d(i,j)=min(d(i+1,j)+dist(i,i+1),d(i+1,i)+dist(j,i+1))
递推边界处理:d(n-1,j)=dist(n-1,n)+dist(j,n)

*/

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>

using namespace std;

const int maxn=1005;//题目没有给出数据量范围,看人家搞这么大,我就也搞这么大吧!!

int n;
double d[maxn][maxn];

typedef struct{
    double x;
    double y;
}P;

P p[maxn];

double dist(int i,int j){
    return sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y));
}

int main()
{
    while(cin>>n){
        for(int i=1;i<=n;i++){
            cin>>p[i].x>>p[i].y;
        }
        memset(d,0,sizeof(d));
        for(int i=1;i<n-1;i++){
            d[n-1][i]=dist(n-1,n)+dist(i,n);
        }
        //由于i>j最终只能递推出d(2,1),又d(1,1)=d(2,1)+dist(2,1)这样可以算出最终结果d(1,1)
        for(int i=n-2;i>=1;i--){
            for(int j=1;j<i;j++){
                d[i][j]=min(d[i+1][j]+dist(i,i+1),d[i+1][i]+dist(j,i+1));
            }
        }
        printf("%.2f\n",d[2][1]+dist(2,1));
    }
    return 0;
}

【记忆化搜索(递归+记忆)】

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>

using namespace std;

const int maxn=1005;//题目没有给出数据量范围,看人家搞这么大,我就也搞这么大吧!!

int n;
double d[maxn][maxn];

typedef struct{
    double x;
    double y;
}P;

P p[maxn];

double dist(int i,int j){
    return sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y));
}

double dp(int i,int j){
    if(d[i][j]>0) return d[i][j];
    return d[i][j]=min(dp(i+1,j)+dist(i,i+1),dp(i+1,i)+dist(j,i+1));
}

int main()
{
    while(cin>>n){
        for(int i=1;i<=n;i++){
            cin>>p[i].x>>p[i].y;
        }
        memset(d,0,sizeof(d));
        for(int i=1;i<n-1;i++){
            d[n-1][i]=dist(n-1,n)+dist(i,n);
        }
        printf("%.2f\n",dp(1,1));
    }
    return 0;
}

【发现】
笔者发现在C++11中,printf输出浮点数,如果使用%lf,会出现迷之错误,用%f则正确!!!
这里写图片描述
这里写图片描述

猜你喜欢

转载自blog.csdn.net/ccnuacmhdu/article/details/81204625