表现良好的最长时间段[问题转换单调栈Java]

1124. 表现良好的最长时间段

给你一份工作时间表 hours,上面记录着某一位员工每天的工作小时数。

我们认为当员工一天中的工作小时数大于 8 小时的时候,那么这一天就是「劳累的一天」。

所谓「表现良好的时间段」,意味在这段时间内,「劳累的天数」是严格 大于「不劳累的天数」。

请你返回「表现良好时间段」的最大长度。

示例 1:

输入:hours = [9,9,6,0,6,6,9]
输出:3
解释:最长的表现良好时间段是 [9,9,6]。

提示:

  • 1 <= hours.length <= 10000
  • 0 <= hours[i] <= 16

分析:

首先暴力肯定过不了全部,因为复杂度过高。暴力思路:遍历从i=[0,len-1],然后以i为段的左端,在遍历一个j从[i,len-1],作为段的右端,维护一个长短即可。

问题其实有比较抽象的地方,也可以化简下,提问求一个时间段,这个时间段中,「劳累的天数」严格 大于「不劳累的天数」,那么怎么去计算一段时间内,>8的天数与<=8的天数大小呢?这里面数字还可能不一样,所以先预处理,令所有>8都变成1,

所有<=8变成0,

其实通常会想到前缀和,来优化计算暴力法中的一个段[i,j]内的情况。i到j中只要前缀和preSum[j]>preSum[i]是不是就是对的?没错,这样就是符合题意的个答案。题目求一个最长的时间段,那么从后往前遍历,问题就转换为了上一题求最大宽度的问题了。

那么也就需要一个单调栈来协助问题的解决的,同样,需要一个单调递减的栈。

首先要明确我们用的前缀和的差,arr[i]-arr[j].是指   [i+1,j]这个区间内的!这点很重要。

所以,最好的情况,就是开一个前缀和数组,int[] arr = new int[len+1]。从1开始计数。

单调减的栈中,存入0,确保原数组可以包含1,就是-0的情况。

    public int longestWPI(int[] hours) {
    	int len = hours.length;
    	if(len<2)return 0;	// 特判
    	// 预处理,将>8变成1,否则变成0
    	int[] arr = new int[len+1];
    	for(int i = 0 ; i < len ; i++){
    		if(hours[i]>8){
    			arr[i+1]=1;
    		}else
    			arr[i+1]=-1;
    	}
    	// 前缀和
    	for(int i = 2 ; i <= len ; i++){
    		arr[i]+=arr[i-1];
    	}
    	// 维护一个单调递减栈
    	Stack<Integer> stack = new Stack<Integer>();
    	stack.add(0);	// 维护下标,首先第一个肯定是在里面的
    	for(int i = 1 ; i <= len ; i++){
    		if(arr[i]<arr[stack.peek()]){
    			stack.add(i);
    		}
    	}
    	// 从后朝前遍历
    	int res = 0;
    	for(int i = len ; i > 0 ; i--){
    		while(!stack.isEmpty() && arr[i]>arr[stack.peek()]){
    			if(i<=stack.peek()){   // 避免i出现在stack.peek左侧的情况,
    				stack.pop();
    				continue;
    			}// 0 -1 -2 -1
    			res = Math.max(res, i-stack.pop());
    		}
    	}
    	return res;
    }

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值