动态规划实战练习1-Help Jimmy
原题摘录-初步分析
一只老鼠,在一个初始高点通过中间的平台一步步往下跳,到地面就算成功.需要我们计算出最小花费的时间,由于速度一定,其实就是一个最短路径的问题.
仔细阅读-进一步理解题意
第二遍读题,更深入的理解问题,其实就是获得更加细节的问题.速度始终是1m/s,那么路程即时间.每次下落高度不能超过MAX,就是说,如果你走的过程中发生了下落高度超过MAX,那么就gameover.(我们在编写程序的时候,逻辑上要保证每种情况都遍历过,只有这样才能是最优的,哪怕是这种明显不可能当作一次尝试的方法,因为计算机不会像人脑那样去自动排除).
接下来,看一下输入输出数据:官方给出的样例能很好的帮助理解题意.
然后可以根据数据画一个平面图,基本就能完全理解题意了.
决胜之战-确定数据结构和算法
到了这里,相信不用看题目,别人问你这个题的意思,也能给他以自己的理解方式复述一遍.
但是,这一步也是将自己的理解转化为计算机思维的一步,所以比较不易.
首先,我们发现每个平台都有起始位置,终端位置,和高度这三个属性.进一步,抽象,地面只不过是一个起始位置巨小,终端位置巨大,高度为0的平台罢了.甚至也可以把Jimmy看作一个平台,他是一个高度已知的距离为0的平台,那又是什么呢?不就是一个点吗.还记得高中的物理抽象模型质点吗?就是他,哈哈.那么,显然它的起始位置一样.
因此,我们选用包含三个属性的结构体platform(平台的意思,起名字要有实际意义).
typedef struct platform{
int x1;//平台起点
int x2;//平台终点
int high;//高度
}Platform;
那么,算法呢?这需要一定的算法知识,如果没有一定算法基础知识的话,建议可以先看一下分类:算法导论学习的一些文章.
通过对平台的抽象,我们可以知道如果N=3的话,那么平台数总共就有N+2=5个.因为我们要保证下一个平台必须比当前jimmy所在的平台高或低,所以我们需要对所有平台进行排序(排序是非常重要的算法之一,这里是结构体排序).使得各平台按高度有序排列.(那到底是按由高到低,还是由低到高呢?)
从整个题目来看,要求从最高点N=5到地面N=1的路程最短.试想:如果我们直到N=4的最短路程f(4),那么f(5)不就等于N=5落到N=4的高度+向左或向右所走的路程+f(4);以此类推f(4)=N=4落到N=3的高度+向左或向右所走的路程+f(3);最终也就是说我们只需要知道f(1)就行了.
那这是什么算法呢,将原问题拆成类似的子问题,而且每个子问题都具有最优子结构,确定了边界(最高,最低),找到了一个能够描述抽象出问题的方程式.没错,他就是DP.
那状态是什么呢?二维数组是我们经常在DP问题中记录状态的结构.当然,不是什么时候都用二维数组.他需要看状态有几个变量构成.两个的话,就用二维数组.
那么具体问题具体分析,这里的状态有几个变量呢?回看刚推的公式.明显有一个变量N也就是第几个平台,还有一个就是向左还是向右(虽然这个变量只有两个值).所以我们可以定义一个二维数组dp[i][j];如果j=0,就是向左,j=1就是向右.比如说:dp[2][1]所代表的含义就是:以2号平台为起点向右到达地面的最短距离.
编写伪代码-搭好框架
int ShortestTime (){
for (int i=1; i<=N+1; ++i){//从低到高,因为只有知道了底层才能递推出高一层的
LeftMinTime (i);
RightMinTime (i);
}
return Min (dp[N+1][0], dp[N+1][1]);//返回最高层到地面的最短距离
}
void RightMinTime (int i){ //以向右为例
if(平台i下面有平台且两者相距不超过MAX){
if (平台i与平台i-1之间满足下落条件就是能接住){
平台i右边到地面的最短时间= 两平台间高度 +
Min (平台i向左需要移动的距离+ 平台i-1向左到地面的最短距离,
平台i向右需要移动的距离 + 平台i-1向右到地面的最短距离);
return;
}
else//不满足下落条件
继续往下找下一个平台
}
}
if (两者相距超过了MAX)
dp[i][1] = INF;
else//平台i右边下面没有平台,说明到底,直接加上自己所在位置的高度
dp[i][1] = plat[i].high;
}
正式编程-测试完善
#include <bits/stdc++.h>
using namespace std;
#define MAXN 1010
#define INF 9000000
typedef struct platform{
int x1;
int x2;
int high;
}Platform;
int compare (const void * p, const void * q){
Platform * p1 = (Platform *)p;
Platform * q1 = (Platform *)q;
return p1->high - q1->high;
}
int Min (int a, int b){
return (a < b) ? a : b;
}
int N, X, Y, MAX;
Platform plat[MAXN];
int dp[MAXN][2]; //dp[i][0]、dp[i][1]分别表示从第i个平台左、右边到地面的最短时间
void LeftMinTime (int i){ //计算从平台i左边到地面的最短时间
int k = i - 1;
while (k > 0 && plat[i].high - plat[k].high <= MAX){
//如果平台i左边下面有平台,且两者相距不超过MAX
if (plat[i].x1 >= plat[k].x1 && plat[i].x1 <= plat[k].x2){
dp[i][0] = plat[i].high - plat[k].high +
Min (plat[i].x1 - plat[k].x1 + dp[k][0], plat[k].x2 - plat[i].x1 + dp[k][1]);
return;
}
else
--k;
}
//如果平台i左边下面没有平台,或者两者相距超过了MAX
if (plat[i].high - plat[k].high > MAX)
dp[i][0] = INF;
else
dp[i][0] = plat[i].high;
}
void RightMinTime (int i){ //计算从平台i右边到地面的最短时间
int k = i - 1;
while (k > 0 && plat[i].high - plat[k].high <= MAX){
//如果平台i右边下面有平台,且两者相距不超过MAX
if (plat[i].x2 >= plat[k].x1 && plat[i].x2 <= plat[k].x2){
dp[i][1] = plat[i].high - plat[k].high +
Min (plat[i].x2 - plat[k].x1 + dp[k][0], plat[k].x2 - plat[i].x2 + dp[k][1]);
return;
}
else
--k;
}
//如果平台i右边下面没有平台,或者两者相距超过了MAX
if (plat[i].high - plat[k].high > MAX)
dp[i][1] = INF;
else
dp[i][1] = plat[i].high;
}
int ShortestTime (){
int i;
for (i=1; i<=N+1; ++i){
LeftMinTime (i);
RightMinTime (i);
}
return Min (dp[N+1][0], dp[N+1][1]);
}
int main(void){
int t;
int i;
while (scanf ("%d", &t) != EOF){
while (t-- != 0){
scanf ("%d%d%d%d", &N, &X, &Y, &MAX);
for (i=1; i<=N; ++i){
scanf ("%d%d%d", &plat[i].x1, &plat[i].x2, &plat[i].high);
}
plat[0].high = 0;
plat[0].x1 = -20000;
plat[0].x2 = 20000;
plat[N+1].high = Y;
plat[N+1].x1 = X;
plat[N+1].x2 = X;
//根据平台高度按从低到高排序
qsort (plat,N+2,sizeof(Platform),compare);
printf ("%d\n", ShortestTime());
}
}
return 0;
}
如果还有不理解的地方,可在下方评论区留言,会在看到后第一时间回复.手打不易,在线求赞!