【dp每日一题】POJ 1661 Help Jimmy
POJ 1661 Help Jimmy
大意:
一个小球从\((x,y)\)位置落下,下落速度恒定为1,当落到一个平台时可以向左也可以向右走,速度也是1,走到边缘时继续下落,每次下落距离不能超过k米,现在给出n个平台的左右边缘位置和高度,问小球最快多久能落到地面
思路:
首先将平台按照高度排一下序,然后计算一下小球第一次碰到的是哪个平台,然后从这个平台开始更新dp数组。\(dp[i][0]\)代表到达第i个平台的左边缘最快时间是多少,\(dp[0][1]\)代表到达右边缘。
可以从上到下写也可以从下到上写,从上到下写需要判断dp数组是否为INF,如果为INF代表不能到达第i个平台,那么就不能利用这个平台去更新下面的平台
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
using namespace std;
const int N = 1e3 + 5;
typedef long long LL;
int t, n, x, y, k, start = 0,dp[N][2],res;
struct node {
int l, r, h;
} a[N];
const int INF = 0x3f3f3f3f;
bool cmp(node a, node b) { return a.h > b.h; }
int main() {
cin >> t;
while (t--) {
cin >> n >> x >> y >> k;
for (int i = 0; i < n; i++) {
cin >> a[i].l >> a[i].r >> a[i].h;
}
memset(dp, 0x3f, sizeof dp);
sort(a, a + n, cmp);
res = INF;
a[n].l = -INF;
a[n].r = INF;
a[n].h = 0;
for (int i = 0; i <= n; i++) {
if (a[i].r < x || a[i].l > x || a[i].h > y || y - a[i].h > k)
continue;
else {
start = i;
break;
}
}
if (start == n) {
cout << y << endl;
continue;
}
dp[start][0] = abs(y-a[start].h)+abs(x - a[start].l);
dp[start][1] = abs(y-a[start].h)+abs(x - a[start].r);
for (int i = start; i < n; i++) {
if (dp[i][0] == INF) continue;
int flag1 = 0;
int flag2 = 0;
for (int j = i + 1; j < n && (flag1 + flag2 != 2); j++) {
if (a[j].r < a[i].l || a[j].l > a[i].r || a[i].h - a[j].h > k)
continue;
else {
if(flag1==0){
if(a[j].l<=a[i].l){
flag1 = 1;
dp[j][0]=min(dp[j][0],dp[i][0]+abs(a[i].l-a[j].l)+abs(a[i].h-a[j].h));
dp[j][1]=min(dp[j][1],dp[i][0]+abs(a[i].l-a[j].r)+abs(a[i].h-a[j].h));
}
}
if(flag2==0){
if(a[j].r>=a[i].r){
flag2 = 1;
dp[j][0]=min(dp[j][0],dp[i][1]+abs(a[i].r-a[j].l)+abs(a[i].h-a[j].h));
dp[j][1]=min(dp[j][1],dp[i][1]+abs(a[i].r-a[j].r)+abs(a[i].h-a[j].h));
}
}
}
}
if (flag1 == 0 && (a[i].h <= k)) res = min(res, dp[i][0] + a[i].h);
if (flag2 == 0 && (a[i].h <= k)) res = min(res, dp[i][1] + a[i].h);
}
cout << res << endl;
}
}