天天看点

LeetCode 1124. 表现良好的最长时间段--前缀和+二分查找

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

给你一份工作时间表 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;
    }
};
           
LeetCode 1124. 表现良好的最长时间段--前缀和+二分查找