动态规划实战练习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;
}

输出显示
如果还有不理解的地方,可在下方评论区留言,会在看到后第一时间回复.手打不易,在线求赞!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值