题目链接:http://poj.org/problem?id=1661
题目大意:
小明0时刻在空中的一个点,要往下掉,掉到某个板子上之后,要往左和右走,走的速度1米/秒,掉落的速度也是1米/秒。但是跳跃的高度不能超过Max,给小明的初始点和高度,还有每个板子的左右端点和高度。问最少多长时间到地面。
题目思路:
也是搜了别人的题解才明白哈哈。写一份更容易看懂的。到达每个板子都有两个状态,一个是往左走,一个是往右走,那么dp【1005】【2】,dp【i】【j】是板子按照高度排序后到达第 i 个板子后, i 号板子的下一个板子走下下个需要最少花多长时间。也就是在i的下一个板子上最短花了多长时间,j==0就往左走,j==1就往右,一定要清楚dp【i】【j】是在i的下一个要到达的板子上花的时间,这样做有个好处就是,可以把起点看作一个长度为0的板子,所以就可以直接状态转移到这个“板子”上,因为结合上边那句话,这个长度为0的板子存的是下一个要到达板子左右走花的最短时间,而这个“板子上”又正好不花时间。这个转移很精妙。
懂了的话可以直接看代码了,下边有递推细节。
备注: 怎么递推???
假如当前在i号板子,那么从i-1到0号板子遍历,看是否存在i号板子往左跳高度小于Max的能落在的板子上
☝如果没有,判断这个i号板子是否能直接落在地上而且高度小于Max,更新dp【i】【0】=0;(到地面之后不能再移动)
?如果有,就要开始状态转移,因为dp【i】【0】存的是在下一个板子上花的时间,那么可以在纸上画一下,下一个板子可能向左也可能向右,一定一定要注意那个dp【i】【0】存的是i的下一个板子上活动的时间。那么下一个板子可能左走也可能右右。
dp [ i ] [ 0 ] = min ( dp [ left ] [ 0 ] + C [ i ] . l - C [ left ] . l , dp [ left ] [ 1 ] + C [ left ] . r - C [ i ] . l );
同理,往右走也来一遍上述过程就好了。
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
struct Point
{
int l,r,h;
}C[1005];
int dp[1005][2];
bool cmp(Point a,Point b){
return a.h<b.h;
}
int main()
{
int t;
cin>>t;
while(t--){
int n,x,y,Max;
cin>>n>>x>>y>>Max;
C[n].l=x;C[n].r=x;C[n].h=y;
for(int i=0;i<n;i++){
cin>>C[i].l>>C[i].r>>C[i].h;
}
sort(C,C+n,cmp);
memset(dp,0x3f,sizeof(dp));
for(int i=0;i<=n;i++){
int left=-1,right=-1;
for(int j=i-1;j>=0;j--){
if(left==-1&&C[j].l<=C[i].l&&C[j].r>=C[i].l&&C[i].h-C[j].h<=Max){
left=j;
}
if(right==-1&&C[j].l<=C[i].r&&C[j].r>=C[i].r&&C[i].h-C[j].h<=Max){
right=j;
}
}
if(left==-1){
if(C[i].h<=Max)dp[i][0]=0;
}
else{
dp[i][0]=min(dp[left][0]+C[i].l-C[left].l,dp[left][1]+C[left].r-C[i].l);
}
if(right==-1){
if(C[i].h<=Max)dp[i][1]=0;
}
else{
dp[i][1]=min(dp[right][0]+C[i].r-C[right].l,dp[right][1]+C[right].r-C[i].r);
}
}
cout<<dp[n][0]+y<<endl;
}
}