题意:
从一个初始位置往下跳,中间有平台,问落到平台上的最小时间是多少
思路:
一开始想着dp[i][2]表示到达第i个平台时,上一步是从左或者右转移过来,但是这样的话状态有点难搞。
这题充分体现了DP的子问题重叠性,对于从第一个点到达终点的过程中,到底怎么分解才可以保证状态转移是方便的,为了排除转移时的状态冗杂,容易想到从一个节点跳下去的时候,只能被最先接到的平台接到,所以思考状态转移的思维方式的时候,两种都要考虑
1.当前状态如何由前一个状态转移过来
2.这个状态往下转移能怎么转移
依据题目选取最优的转移方式是必须的
该题很明显是第二种更好想
从第一个到终点可以分解为从第二个到终点…从第n个平台到终点的最短时间,所以从小的子问题倒序遍历,关键在于这样的状态转移选择第一次遇到能够跳下去的时候就可以停止了,与我一开始想的从上面转移下来的冗杂性完全不一样
所以dp[i][2]表示从第i个平台出发,往左或往右到终点的最小值,排序就按高度从小到大排,然后顺序遍历
代码如下
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1020;
struct Node{
int l,r,h;
bool operator<(const Node&a)const
{
return h<a.h;
}
}node[maxn];
int dp[maxn][2];
int main()
{
int t,n,l,r,maxh;
scanf("%d",&t);
while(t--)
{
memset(dp,0x3f,sizeof(dp));
scanf("%d%d%d%d",&n,&l,&r,&maxh);
for(int i=0;i<n;++i)
scanf("%d%d%d",&node[i].l,&node[i].r,&node[i].h);
node[n].l=l,node[n].r=l,node[n].h=r;
sort(node,node+n);
int lp,rp;
for(int j=0;j<=n;++j)
{
lp=rp=-1;
for(int k=j-1;k>=0;--k)
{
if(lp==-1&&node[k].l<=node[j].l&&node[k].r>=node[j].l)
lp=k;
if(rp==-1&&node[k].l<=node[j].r&&node[k].r>=node[j].r)
rp=k;
}
if(lp==-1&&node[j].h<=maxh)//左下方没有但能落地时
dp[j][0]=node[j].h;
if(rp==-1&&node[j].h<=maxh)//右下方没有但能落地时
dp[j][1]=node[j].h;
if(lp!=-1&&node[j].h-node[lp].h<=maxh)
dp[j][0]=min(dp[lp][0]+node[j].l-node[lp].l,dp[lp][1]+node[lp].r-node[j].l)+node[j].h-node[lp].h;
if(rp!=-1&&node[j].h-node[rp].h<=maxh)
dp[j][1]=min(dp[rp][1]+node[rp].r-node[j].r,dp[rp][0]+node[j].r-node[rp].l)+node[j].h-node[rp].h;
}
printf("%d\n",dp[n][0]);
}
return 0;
}