POJ1661.Help Jimmy(逆向DP)

题目链接:http://poj.org/problem?id=1661
题意:老鼠从(x,y)位置处跳往地面,有n个平台,老鼠每移动到一个平台的边缘会落下,每次最大下落的高度不可以超过max(当老鼠从一个平台落到另一个平台的边缘时,可以视为落在这个平台上),老鼠每下降一个高度或者横向移动一个单位耗时1,问落到地面最短时间
解题思路:先将平台按高度从大到小排序,利用逆向,从地面开始,因为这样终点都是dp[0][0]和dp[0][1],dp[i][0]表示从地面到第i个平台的左边最短时间,dp[i][1]表示从地面到第i个平台的右边的最短时间。
分两种情况处理dp[i][0]和dp[i][1],
转移方程:
以dp[x][0]为例,寻找到达x台阶左边界的最小时间
遍历寻找第x个台阶落下后到达的台阶y,然后比较y的左边缘和右边缘到x个台阶的最小时间即可

for(i=x+1;i<=n;i++){
		if(step[x].h-step[i].h>maxh) break;
		if(step[i].x1<=step[x].x1&&step[i].x2>=step[x].x1){
			dp[x][0]=min(dp[i][0]+step[x].x1-step[i].x1,dp[i][1]+step[i].x2-step[x].x1);
			return ;
		}
	}
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define inf 99999999
int n,x,y,maxh;
int t;
struct node{
	int x1,x2,h;
	bool operator < (node a){
		return h>a.h;
	}
}step[1010];
int dp[22000][2];
int pos[22000][2];
void left(int x){
	int i;
	for(i=x+1;i<=n;i++){
		if(step[x].h-step[i].h>maxh) break;
		if(step[i].x1<=step[x].x1&&step[i].x2>=step[x].x1){
			dp[x][0]=min(dp[i][0]+step[x].x1-step[i].x1,dp[i][1]+step[i].x2-step[x].x1);
			return ;
		}
	}
	if(step[x].h-step[i].h>maxh)
		dp[x][0]=inf;
	else
		dp[x][0]=0;
}
void right(int x){
	int i;
	for(i=x+1;i<=n;i++){
		if(step[x].h-step[i].h>maxh) break;
		if(step[i].x1<=step[x].x2&&step[i].x2>=step[x].x2){
			dp[x][1]=min(dp[i][0]+step[x].x2-step[i].x1,dp[i][1]+step[i].x2-step[x].x2);
			return ;
		}
	}
	if(step[x].h-step[i].h>maxh)
		dp[x][1]=inf;           //超过maxh最大下落距离
	else
		dp[x][1]=0;    //直接到达地面
}
int main(){
	cin>>t;
	while(t--){
		memset(step,0,sizeof(step));
		memset(pos,0,sizeof(pos));
		cin>>n>>x>>y>>maxh;
	    for(int i=1;i<=n;i++)
			scanf("%d%d%d",&step[i].x1,&step[i].x2,&step[i].h);
		sort(step+1,step+1+n);
		step[0].x1=step[0].x2=x;  //将初始位置视为一个长度为0的台阶
		step[0].h=y;
		step[n+1].x1=-22000; step[n+1].x2=22000; step[n+1].h=0;
		for(int i=n;i>=0;i--){
			left(i);
			right(i);
		}
		printf("%d\n",min(dp[0][0]+y,dp[0][1]+y));
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/littlegoldgold/article/details/107245435