⭐北邮复试刷题1793. 好子数组的最大分数___(基于快排的划分思想/基于快排的划分思想的优化过程/基于贪心的双指针操作)__每日一题

Problem: 1793. 好子数组的最大分数

文章目录


在这里插入图片描述

思路

法一: 基于快排的划分思想

1.即开始为拿到数组全部 计算分数 后来对每次找到的min值的下标左右两侧进行划分 即将min去掉 从而可以构建出两个新数组;
2.对新数组继续计算分数 与前一次比较取最大 接着继续找到min值的下标 继续划分;
3.直到划分的所有数组均结束或下标出现非法继而停止则结束此过程 则此时max即为最大;

法二: 基于快排的划分思想的优化过程

1.优化划分过程 对多个min可同时划分 而不是一个一个划分;
2.即如5 5 1 4 5 4 1 —> 5 5 | 1 | 4 5 4 | 1 故只需检查5 5 和 4 5 4 即可;

法三: 基于贪心的双指针操作

1.思路即注意到好子数组一定是包括nums[k] 因此我们可以以其作为起点开始枚举;
2.若其左方为>=nums[k] 则左方可直接延长; 若其右方为>=nums[k] 则右方同理可直接延长;
3.之所以可直接延长 是因分数中min恒为nums[k] 且长度变长 因此分数是一定为增加方向的;
4.但是上述只是一种可能 因可能左右继续延长使得min为小于nums[k] 但长度增大很多 同样也可使得分数为增加方向;
5.因此我们需要枚举这几种可能 即发现只需要遍历一遍数组即可;

具体思路见代码注释;

Code:

// 法一: 基于快排的划分思想 
// 即开始为拿到数组全部 计算分数 后来对每次找到的min值的下标左右两侧进行划分 即将min去掉 从而可以构建出两个新数组
// 对新数组继续计算分数 与前一次比较取最大 接着继续找到min值的下标 继续划分 
// 直到划分的所有数组均结束或下标出现非法继而停止则结束此过程 则此时max即为最大
// class Solution {
//     int max = -1;
//     public int maximumScore(int[] nums, int k) {
//         int len = nums.length;
//         int start = 0;
//         int end = len-1;
//         cutNums(nums,start,end,k);
//         return max;
//     }

//     public void cutNums(int[] nums,int start,int end,int k){
//         // k做下标检查工作
//         if(start <= end && start <= k && end >= k){
//             int[] message = calNumsOfMinAndIndexAndSum(nums,start,end);
//             int min_temp = message[0];
//             int min_index = message[1];
//             int temp_sum = message[2];
//             max = Math.max(max,temp_sum);
//             cutNums(nums,start,min_index-1,k);
//             cutNums(nums,min_index+1,end,k);
//         }
//         // 一旦下标已经非法 则此段数组直接舍弃即可 因其再怎么移动也无法合法 因其移动为缩小方式
//         return ;
//     }

//     // 实际上 可以将每次得到的所有最小值都去除 这里只是模拟找到一个最小值下标 依据这一个进行切分的情况
//     public int[] calNumsOfMinAndIndexAndSum(int[] nums,int start,int end){
//         int[] mes = new int[3];
//         // mes[0] = nums[start];
//         // mes[1] = start;
//         // for(int i=start+1;i<=end;i++){
//         //     if(nums[i] < nums[i-1]){
//         //         mes[1] = i;
//         //         mes[0] = nums[i];
//         //     }
//         // }
//         // 下述方式避免了此段数组只有一个元素的情况 因当start==end时 使用上述则不会进入循环
//         mes[0] = Integer.MAX_VALUE;
//         mes[1] = -1;
//         for(int i=start;i<=end;i++){
//             if(nums[i] < mes[0]){
//                 mes[1] = i;
//                 mes[0] = nums[i];
//             }
//         }
//         mes[2] = mes[0]*(end-start+1);
//         return mes;
//     }
// }


// 法二: 基于快排的划分思想的优化过程
// 优化划分过程 对多个min可同时划分 而不是一个一个划分
// 即如5 5 1 4 5 4 1  --->  5 5 | 1 | 4 5 4 | 1    故只需检查5 5 和 4 5 4 即可
// class Solution {
//     int max = -1;
//     public int maximumScore(int[] nums, int k) {
//         int len = nums.length;
//         int start = 0;
//         int end = len-1;
//         cutNums(nums,start,end,k);
//         return max;
//     }

//     public void cutNums(int[] nums,int start,int end,int k){
//         // k做下标检查工作
//         if(start <= end && start <= k && end >= k){
//             List<Integer> list = calNumsOfMinAndIndexAndSum(nums,start,end);
//             int list_len = list.size();
//             max = Math.max(max,nums[list.get(0)]*(end-start+1));
//             for(int i=0;i<list_len;i++){
//                 int index = list.get(i);
//                 cutNums(nums,start,index-1,k);
//                 cutNums(nums,index+1,end,k);
//             }
//         }
//         // 一旦下标已经非法 则此段数组直接舍弃即可 因其再怎么移动也无法合法 因其移动为缩小方式
//         return ;
//     }

//     // 实际上 可以将每次得到的所有最小值都从数组中去除 根据去除的"豁口"划分出新的数组
//     // 即如5 5 1 4 5 4 1  --->  5 5 | 1 | 4 5 4 | 1    故只需检查5 5 和 4 5 4 即可
//     // 因一旦某段数组中带有此min 则其所得分数必定是还不如原先分数! 因其min和原先的相同 且长度又缩短了 故定不会实现分数增加
//     public List<Integer> calNumsOfMinAndIndexAndSum(int[] nums,int start,int end){
//         int min = Integer.MAX_VALUE;
//         for(int i=start;i<=end;i++){
//             min = Math.min(min,nums[i]);
//         }
//         List<Integer> list = new ArrayList<>();
//         for(int i=start;i<=end;i++){
//             if(nums[i] == min){
//                 list.add(i);
//             }
//         }
//         return list;
//     }
// }


// 法三: 基于贪心的双指针操作
// 思路即注意到好子数组一定是包括nums[k] 因此我们可以以其作为起点开始枚举 
// 若其左方为>=nums[k] 则左方可直接延长; 若其右方为>=nums[k] 则右方同理可直接延长;
// 之所以可直接延长 是因分数中min恒为nums[k] 且长度变长 因此分数是一定为增加方向的
// 但是上述只是一种可能 因可能左右继续延长使得min为小于nums[k] 但长度增大很多 同样也可使得分数为增加方向
// 因此我们需要枚举这几种可能 即发现只需要遍历一遍数组即可
class Solution {
    public int maximumScore(int[] nums, int k) {
        int len = nums.length;
        // 已经认为把nums[k]为置入状态 故l和r要枚举k的左右两方
        int l = k-1;
        int r = k+1;
        int score = 0;
        // i即维护此时数组中min值 初始即为nums[k] 因好子数组一定为包含nums[k]
        for(int i=nums[k];;){
            // 若能移动则移动 因此时min未变化 且长度为增大 故分数也为增大 
            while(l >=0 && nums[l] >= i){
                l--;
            }
            // 若能移动则移动 因此时min未变化 且长度为增大 故分数也为增大
            while(r < len && nums[r] >= i){
                r++;
            }
            // 求出移动后的分数
            score = Math.max(score,i*((r-1)-(l+1)+1));
            // 边界判断 若l r均出界则直接停止
            if(l < 0 && r >= len)
                break;

            // 此时此种情况下的最大分数已经计算完毕 因此需要试探下一种情况
            // 下一种情况即min进行变化(选择移动到不太小于他的元素) 再使长度增加 此时也有可能超过原先的分数 
            // 由于之前的两个while操作 故此时l和r下标对应的两个元素值都是大于之前的标准i的 
            // 我们选择较大的让min值移动
            if(l >=0 && r < len)
                i = Math.max(nums[l],nums[r]);
            else if(l == -1)
                i = nums[r];
            else 
                i = nums[l];
        }
        return score;
    }
}
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

向光.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值