(补充上篇)超级码力-第三题-大楼间穿梭 动态规划 单调栈
hexo博客:blog.little-star.top
周日-晴-过得跟昨天差不多-今天也参赛还是没进前300-下周还有两次机会-明天师弟师妹军训我们要开始做实践周项目。
提示:题目中“用蛛丝移动到右侧k栋建筑中第一栋比当前位置高的大楼”,应该改为大于等于当前位置的大楼。
单调栈
解答这题的时候用到了一个解题界常说的数据结构——单调栈,即元素只能按单调递增或单调递减的顺序进栈或出栈。
在这里的应用就是可以记录蜘蛛侠使用蛛丝在当前位置能跳到的最远位置(视野范围内),即记录右侧第一座比当前位置高或相等的大楼位置。
动态规划
这里需要求体力消耗的最小值,最优解为题,一看就知道是老动态规划了,我们这里采用求出跳到每个位置消耗的最小体力,理所当然能求出跳到最后位置消耗的最小体力。
解答
以下代码为我参考各位大佬的c++解答自己写出的Java解答。
Java栈模板类
/** * Stack * boolean empty() 返回此栈是否为空 * E peek() 查看此栈顶部的对象,而不从栈中删除它 * E pop() 删除此栈顶部的对象,并将该对象作为此函数的值返回 * E push(E item) 将对象item压到栈的顶部 * int search(Object o) 返回一个对象在此栈的位置,顶部位置为1,往下加一,找不到返回-1 */
public static long shuttleInBuildings(int[] heights, int k, int x, int y) {
// write your code here.
Stack<Integer> s = new Stack<>(); //单调栈,该题单调栈元素需要从大到小入栈。
int n = heights.length;
int []rightTall = new int[n]; //该数组记录向右第一座比当前位置高或相等的位置。
for(int i = 0; i<n ; i++) rightTall[i] = -1;
for(int i = n-1; i>= 0 ; i--){
//当当前位置i的高度高于栈顶高度时,出栈,直到栈顶没有元素或者栈为空。
//因为当当前位置为最高时,蜘蛛侠不能使用蛛丝。
while(!s.empty() && heights[s.peek()] < heights[i])
s.pop();
//如果此时栈非空且栈顶元素的位置在视野内,则栈顶位置即使当前位置蛛丝到达的位置。
if(!s.empty() && s.peek()-i <= k)
rightTall[i] = s.peek();
//元素进栈,此时该元素一定是栈中最小的元素,因为比该元素大的元素都弹出来了。
s.push(i);
}
//System.out.println(Arrays.toString(rightTall));
//求解体力的最小值用到动态规划的思想
long []dp = new long[n]; //记录移动到每个位置的最小体力。
//初始化每个位置为最大值,之后只有取到比这个值小即进行替换。
for(int i = 1 ; i<n ; i++) dp[i] = Long.MAX_VALUE;
//第一个位置不需要体力
dp[0] = 0;
//遍历每一个位置
for(int i = 0 ; i<n ; i++){
if(dp[i] == Long.MAX_VALUE)
continue;
//以下三个条件判断语句的前提都是目标位置的体力值消耗大于执行当前条件跳到目标位置消耗的体力值。
//跳跃到下一座大楼,消耗y体力
if(i+1 < n && dp[i+1] > dp[i]+y)
dp[i+1] = dp[i]+y;
//跳跃到下两座大楼,同样消耗y体力
if(i+2 < n && dp[i+2] > dp[i]+y)
dp[i+2] = dp[i]+y;
//使用蛛丝,如果右侧有楼可跳,消耗x体力
if(rightTall[i] != -1 && dp[rightTall[i]] > dp[i]+x)
dp[rightTall[i]] = dp[i] + x;
}
//System.out.println(Arrays.toString(dp));
//输出最后一个位置记录的体力消耗
return dp[n-1];
}