相信大家都听说过“是男人就下100层”系列游戏,游戏中包括多个长度和高度各不相同的平台,地面是最低的平台,高度为零,长度无限。
一个男人在开始的时候从高于所有平台的某处开始下落,它的下落速度始终为1米/秒。当他落到某个平台上时,游戏者选择让他向左或向右跑,跑动的速度也是1米/秒。当他跑到平台的边缘时会继续下落。要求每次下落的高度不能超过MAX米,不然就会摔死,游戏也会结束。
一个男人在开始的时候从高于所有平台的某处开始下落,它的下落速度始终为1米/秒。当他落到某个平台上时,游戏者选择让他向左或向右跑,跑动的速度也是1米/秒。当他跑到平台的边缘时会继续下落。要求每次下落的高度不能超过MAX米,不然就会摔死,游戏也会结束。
请帮忙设计一个程序,计算最快到达地面所用的时间。
import java.util.*;
public class Main {
public static void main(String[] args) {
@SuppressWarnings("resource")
Scanner in = new Scanner(System.in);
while (in.hasNext()) {
Integer[][] floors = new Integer[in.nextInt()][3];
int X = in.nextInt(), Y = in.nextInt(), MAX = in.nextInt();
int left = Integer.MAX_VALUE, right = Integer.MIN_VALUE;
for (int i = 0; i < floors.length; i++) {
int temp = in.nextInt();
left = Math.min(left, temp);
floors[i][0] = temp;
temp = in.nextInt();
right = Math.max(right, temp);
floors[i][1] = temp;
floors[i][2] = in.nextInt();
}
System.out.println(calc(floors, X, Y, MAX, left, right));
}
}
static int calc(Integer[][] floors, int X, int Y, int MAX, int left, int right) {
// 横坐标对齐
for (int i = 0; i < floors.length; i++) {
floors[i][0] -= left;
floors[i][1] -= left;
}
X -= left;
right -= left;
left = 0;
// 将平台按高度排序,同高度则按横坐标排序
Arrays.sort(floors, new Comparator<Integer[]>() {
@Override
public int compare(Integer[] arg0, Integer[] arg1) {
if (arg0[2].equals(arg1[2]))
return arg0[0] - arg1[0];
return arg0[2] - arg1[2];
}
});
// 如果X不在平台横纵坐标范围,直接掉下来即可(不考虑无解及非法情况)
if (X < 0 || X > right || Y < floors[0][2])
return Y;
// dp求解,第一个值表示到达地面最小时间,第二个值表示当前往下跳的高度
int[][] dp = new int[right + 1][2];
int low = 0, high = floors[0][2];
for (Integer[] floor : floors) {
// 判断进入新的一层
if (high < floor[2]) {
// 上一层右边空档区
int det = high - low;
for (int i = left; i <= right; i++) {
dp[i][0] += det;
dp[i][1] += det;
}
// 平台高于Y值,舍弃(测试用例中貌似没有用到)
if (high > Y) {
high = low;
break;
}
// low、high上升一层
low = high;
high = floor[2];
left = 0;
}
int det = high - low;
// 左边空挡区
for (int i = left; i < floor[0]; i++) {
dp[i][0] += det;
dp[i][1] += det;
}
// 中间平台区,判断从左边、右边能否走通
boolean isLeft = dp[floor[0]][1] + det <= MAX;
boolean isRight = dp[floor[1]][1] + det <= MAX;
for (int i = floor[0]; i <= floor[1]; i++) {
if (!isLeft && !isRight) {
dp[i][1] = MAX + 1;
continue;
}
int goRight = floor[1] - i + dp[floor[1]][0] + det;
// left取的是新值,只有最左边的需要+det
int goLeft = i - floor[0] + dp[floor[0]][0];
if (i == floor[0])
goLeft += det;
if (!isLeft)
dp[i][0] = goRight;
else if (!isRight)
dp[i][0] = goLeft;
else
dp[i][0] = Math.min(goLeft, goRight);
dp[i][1] = 0;
}
// left指针右移
left = floor[1] + 1;
}
return dp[X][0] + Y - high;
}
}