- 表现良好的最长时间段
给你一份工作时间表 hours,上面记录着某一位员工每天的工作小时数。
我们认为当员工一天中的工作小时数大于 8 小时的时候,那么这一天就是「劳累的一天」。
所谓「表现良好的时间段」,意味在这段时间内,「劳累的天数」是严格 大于「不劳累的天数」。
请你返回「表现良好时间段」的最大长度。
示例 1:
输入:hours = [9,9,6,0,6,6,9]
输出:3
解释:最长的表现良好时间段是 [9,9,6]。
提示:
1 <= hours.length <= 10000
0 <= hours[i] <= 16
通过次数8,955
提交次数30,112
题解
简单来说,就是让你找某个区间,这个区间大于8的数字要比小于等于8的数字多,那么可以转换为大于8的数字为1,小于等于8的数字为-1,于是题目转换成了求某个最长区间,使得该区间和大于0。
于是题目又变成了求前缀和,前缀和为sum[x],表示区间[0,x]的和,那么答案就是sum[x]-sum[y]得到区间[y+1,x]的区间和,于是我们要保证该区间和大于0,也就是sum[x]必须大于sum[y],于是我们希望区间越长越好,那么就有两种情况的y。
y1和y2
情况1:sum[y1]<=sum[y2] and y1<y2,那么肯定直接不用保存y2,因为sum[y1]小于等于sum[y2],sum[x]-sum[y2]>0那么肯定sum[x]-sum[y1]>0,并且更靠左边,得到的区间更长。
情况2:sum[y1]>sum[y2] and y1<y2,这个情况需要考虑了,于是需要把y2保存下来,储存到一个动态数组中,于是我们得到一个动态数组q,q中的所有元素严格递减。
那么问题就很简单了,我们针对前缀和sum从左到右遍历,然后针对每个sum[x]:
n=q.szie()-1;
if sum[x]<sum[q[n]]:
q.push_back(x);
else:
y=Find(sum[x]);//Find函数负责找到q中最左边的下标y使得sum[y]<sum[x]
mx=max(mx,x-y);
当然还有特判[0,x]这种情况,如果某个sum[x]大于0,也要比较mx=max(mx,x)。
AC代码
class Solution {
public:
int sum[10010];
vector<int>q;
int Find(int x)//找到最左边大于x的元素下标
{
int l=0,r=q.size()-1,fin=-1;
while(l<=r)
{
int mid=(l+r)/2;
if(sum[q[mid]]<x)
{
r=mid-1;
fin=mid;
}
else l=mid+1;
}
return fin==-1?-1:q[fin];
}
int longestWPI(vector<int>& hours) {
for(int i=0;i<hours.size();i++)
{
if(hours[i]>8)
hours[i]=1;
else
hours[i]=-1;
}
int mx=0;
sum[0]=hours[0];//得到前缀和
if(sum[0]>0)//边缘处理
mx=1;
for(int i=1;i<hours.size();i++)
{
sum[i]=sum[i-1]+hours[i];
if(sum[i]>0)特判
mx=max(mx,i+1);
}
q.push_back(0);
for(int i=1;i<hours.size();i++)
{
int n=q.size()-1;
if(sum[q[n]]>sum[i])//贪心选择
{
q.push_back(i);
}
else
{
int pos=Find(sum[i]);
if(pos==-1)//说明没有找到满足条件的
continue;
mx=max(mx,i-pos);
}
}
return mx;
}
};