题意:
黑书 158 铁球落地。
思路:
1. 出发点题目给了,就是小球的初始位置。目标状态也有了,那就是地面。如果定义转移方程:dp[i][0], dp[i][1] 分别表示第 i 个板子左、右到地面的最小时间;
2. 有了出发点,也有了目标位置,这时候可以利用记忆化搜索。(已知状态,未知值)->(中间状态,更容易求的未知值)->(末状态,已知值);
3. 由于本题的末状态只有一个:地面。所以可以采取逆向思维的方法,把上面的式子倒着推过来,先求离地面最近的,再求我们已知的初始状态;
4. 传统的背包问题是:(已知状态,已知值)->(中间状态)->(末状态,未知值)的一个思路,顺理成章。而本题和前面 UVa 10118 有点类似,
都是一个逆向的思维在里面,不同点是 UVa 10118 末状态未知,而本题是已知的,所以可以转化过来用递归关系式得到解决.
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 1010;
const int INFS = 0x3fffffff;
struct BOARD {
int x1, x2, h;
bool operator < (const BOARD& other) const { return h < other.h; }
} board[MAXN];
int dp[MAXN][2];
int workout(int n, int maxh) {
for (int i = 1; i < n; i++) {
for (int j = i-1; j >= 0; j--) {
if (board[i].x1 >= board[j].x1 && board[i].x1 <= board[j].x2) {
int h = board[i].h - board[j].h;
if (h > maxh) dp[i][0] = INFS;
else if (j == 0) dp[i][0] = h;
else dp[i][0] = min(dp[j][0] + board[i].x1 - board[j].x1, dp[j][1] + board[j].x2 - board[i].x1) + h;
break;
}
}
for (int j = i-1; j >= 0; j--) {
if (board[i].x2 >= board[j].x1 && board[i].x2 <= board[j].x2) {
int h = board[i].h - board[j].h;
if (h > maxh) dp[i][1] = INFS;
else if (j == 0) dp[i][1] = h;
else dp[i][1] = min(dp[j][0] + board[i].x2 - board[j].x1, dp[j][1] + board[j].x2 - board[i].x2) + h;
break;
}
}
}
return dp[n-1][1];
}
int main() {
int cases;
scanf("%d", &cases);
while (cases--) {
int n, x, y, maxh;
scanf("%d%d%d%d", &n, &x, &y, &maxh);
for (int i = 1; i <= n; i++) {
scanf("%d%d%d", &board[i].x1, &board[i].x2, &board[i].h);
if (board[i].x1 > board[i].x2)
swap(board[i].x1, board[i].x2);
}
board[0].x1 = board[0].x2 = x, board[0].h = y;
board[n+1].x1 = -20010, board[n+1].x2 = 20010, board[n+1].h = 0;
n += 2;
sort(board, board + n);
printf("%d\n", workout(n, maxh));
}
return 0;
}