POJ - 1661(Help Jimmy)
题目描述
解题思路
非常有意思的动态规划题。首先思考一个问题,如果最小下落路径已知,那么不管是从高处下落,还是从地面向上起跳,只要路径不变,那么路径长也是不会改变的。所以,理解了这点,就可以解决第一个问题,动态规划的起始点与最终状态。由于从起点下落,起点已知,但是下落的路径有多种可能,所以无法确定最终状态,因此我们可以将起点作为最终状态,由地面向起点动规。
第二个问题就是转移的过程:
如上图,如果从平台1的左端点下落,则只可能掉落到平台2上,而无法穿过平台2掉落到平台3上。
如上图,如果从平台1左端点下落,则由于平台2的右端点在平台1的左侧,即无法从平台1下落到平台2上。
上面介绍了大致的问题,那么下面就可以确定状态表示和动态转移方程了
1.状态表示:
–>dp[i][0]
表示从第i个平台的左端点下落,经过若干平台到达地面的最短时间
–>dp[i][1]
表示从第i个平台的右端点下落,经过若干平台到达地面的最短时间
2.状态转移:
对于第i个平台,人可能从第i个平台的左右两端下落,且只有可能从第i-1~0
个平台(0表示地面)的某个平台转移而来,假设符合条件的平台是平台k
(还要保证不会摔死),则人下落到平台k后,也可能走向左端或者右端。所以有
从第i个平台的左端下落,则必有value[i].l >= value[k].l && value[i].l <= value[k].r
(l表示左端点坐标,r表示右端点坐标)
–>a.从平台k的左端转移而来:dp[k][0] + value[i].l - value[k].l
–>b.从平台k的右端转移而来:dp[k][1] + value[k].r - value[i].l
dp[i][0] = min{a, b} + value[i].h - valu[k].h
(h表示平台离地面高度)
从第i个平台的右端下落,则必有value[i].r >= value[k].l && value[i].r <= value[k].r
–>a.从平台k的左端转移而来:dp[k][0] + value[i].r - value[k].l
–>b.从平台k的右端转移而来:dp[k][1] + value[k].r - value[i].r
dp[i][1] = min{a, b} + value[i].h - valu[k].h
(h表示平台离地面高度)
最终答案即从两种状态中选择最小的,如假设平台n+1是起点res = min{dp[n+1][0], dp[n+1][1]}
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN = 1005;
const int INF = 0x3f;
typedef struct Value{
int l, r; //l表示左端点,r表示平台右端点
int h; //h表示平台高度
}Value;
Value value[MAXN];
bool cmp(Value t1, Value t2) { //按照平台高度从低到高排序
return t1.h < t2.h;
}
int dp[MAXN][2]; //dp[i][0]表示从第i个平台左端落下时,到达地面最短时间,dp[i][1]表示从第i个平台右端落下时,到达地面最短时间
int main() {
int main() {
int t;
cin >> t;
while(t--) {
memset(dp, INF, sizeof(dp));
int N, X, Y, MAXH; //MAXH表示最大下落高度
cin >> N >> X >> Y >> MAXH;
for(int i = 1; i <= N; i++) {
cin >> value[i].l >> value[i].r >> value[i].h;
}
sort(value+1, value+N+1, cmp); //按照高度排序
value[0].l = -20005, value[0].r = 20005; //将地面当作0号平台,且长度无限长
value[0].h = 0;
value[N+1].l = value[N+1].r = X; //将起点当作最后一个平台,其长度为0
value[N+1].h = Y;
dp[0][0] = dp[0][1] = 0;
for(int i = 1; i <= N+1; i++) {
//找寻从左端点落下,可以掉落到的平台位置
int j = i - 1;
while(j >= 0 && value[i].h - value[j].h <= MAXH) {
if(value[i].l >= value[j].l && value[i].l <= value[j].r) {
break;
}
j--;
}
if(value[i].h - value[j].h <= MAXH) { //保证不会摔死的情况下,寻找最优
if(j) { //非地面,所以可能需要走一段路
dp[i][0] = min(dp[j][0] + value[i].l - value[j].l, dp[j][1] + value[j].r - value[i].l) + value[i].h - value[j].h;
}else {
dp[i][0] = value[i].h - value[j].h;
}
}
//找寻从右端点落下,可以掉落到的平台位置
j = i - 1;
while(j >= 0 && value[i].h - value[j].h <= MAXH) {
if(value[i].r >= value[j].l && value[i].r <= value[j].r) {
break;
}
j--;
}
if(value[i].h - value[j].h <= MAXH) { //保证不会摔死的情况下,寻找最优
if(j) { //下落到非地面的平台上,如果是地面,则无需从端点走向下落点
dp[i][1] = min(dp[j][0] + value[i].r - value[j].l, dp[j][1] + value[j].r - value[i].r) + value[i].h - value[j].h;
}else {
dp[i][1] = value[i].h - value[j].h;
}
}
}
cout << min(dp[N+1][0], dp[N+1][1]) << endl;
}
return 0;
}