特殊数组下标最大值(233周赛)(1)

该博客讨论了如何在给定限制条件下构造一个数组,使得指定索引处的值最大化。通过模拟堆叠砖块的过程,算法首先填充数组并逐步增加层数,确保相邻元素差不超过1且总和不超过最大和。在遇到边界时,算法会调整策略以最大化目标索引处的值。文章提供了两种不同的实现方式,均实现了线性或对数时间复杂度的解决方案。
摘要由CSDN通过智能技术生成

给你三个正整数 n、index 和 maxSum 。你需要构造一个同时满足下述所有条件的数组 nums(下标 从 0 开始 计数):

nums.length == n
nums[i] 是 正整数 ,其中 0 <= i < n
abs(nums[i] - nums[i+1]) <= 1 ,其中 0 <= i < n-1
nums 中所有元素之和不超过 maxSum
nums[index] 的值被 最大化
返回你所构造的数组中的 nums[index] 。

注意:abs(x) 等于 x 的前提是 x >= 0 ;否则,abs(x) 等于 -x 。

 过程模拟,从index开始往两边扩充:维护一个[l,r]范围,每次往范围内每个位置+1,通过这种方式维护一个向上生长的“三角形”。

时间复杂度O(logM),其中M为maxSum-n。
空间复杂度O(1)。

对于 M = maxSum - n 减少1 + 3 + 5 + ... 直到 剩余不够 假设共n+1项

1 + 3 + 5 ... + (2n + 1) = (n + 1)(1 + 2n + 1) /2 = (n + 1) ^2 , 有(n + 1)^2 < M

所以 n = log2(M), 时间复杂度为O(log2(M))

public int maxValue(int n, int index, int maxSum) {
    int l = index, r = index;
    int ans = 1;
    // 整个数组一开始全部填充为1,
    // rest记录先全部填充1后,剩下1的个数
    int rest = maxSum - n;
    while (l > 0 || r < n - 1) {
        int len = r - l + 1;
        if (rest >= len) {
            // 当前[l,r]范围全部+1
            rest -= len;
            ans++;
            // 往左右两边扩
            l = Math.max(0, l - 1);
            r = Math.min(n - 1, r + 1);
        } else {
            break;
        }
    }
    // 扩大到整个数组之后,剩余的值“雨露均沾”一下
    ans += rest / n;
    return ans;
}

作者:liberg
链接:https://leetcode-cn.com/problems/maximum-value-at-a-given-index-in-a-bounded-array/solution/shu-xue-guo-cheng-mo-ni-zui-qing-xi-de-j-gz30/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

把每一个数当成一个砖块,目的是堆成形如三角形的台阶,使index所在处的台阶尽可能高。

本解法未使用任何算法,只是简单粗暴的模拟堆台阶的过程,毕竟菜鸡什么算法也不会啊😭😭。

 

class Solution {
public:
    int maxValue(int n, int index, int maxSum) {
      int diff=maxSum-n,left=index,right=index, res=1,dl=0,dr=0; 
      while(diff>0){                     //当还有剩余砖块时
        if(--left>=0) dl++;              //尚未到达左边界
        if(++right<n) dr++;              //尚未到达右边界
        if(left<0&&right>=n){            //当到达左边界和右边界时 及时退出 
          res+=diff%n==0?diff/n:diff/n+1; //把剩余的砖块均分了,直接退出
          return res;
        }
        res+=1;          //层数更新
        diff-=(dl+dr+1); //使顶层堆成严格正三角形所需的砖块数(左边所需+右边所需+index处1个)
      }      
      return res;
    }
};

作者:ivon_shi-a-feng-ya
链接:https://leetcode-cn.com/problems/maximum-value-at-a-given-index-in-a-bounded-array/solution/jian-dan-mo-ni-jiu-ceng-zhi-tai-qi-yu-le-c6f6/

很明显 为使num[index]的值最大,需要使每一列的顶层其尽可能形成为一个严格的三角形(阶梯式),即从index向两边单调递减。

image.png

 

否则,要么不满足要求

image.png

或者产生浪费(当然为了使之后堆的更高也可看作不算浪费)

image.png
模拟从底层向上堆砖块,当在index处添加砖块可以满足要求时,添加即可。
当想在index出添加砖块,却不满足要求时,需要先把周围的方块组成的台阶堆起来。
那么问题 是需要多少块才能把周围堆成台阶呢?

#对于一般情况:
1.由于maxSum>=n,故每列都能先安排一块砖;剩余砖块数为diff=maxSum-n

image.png
2.先堆完一层后,先往index处堆,再往其附近依次堆,左右两个方向同时推进 直到边界
(dl表示堆下一层时 所需要的左侧砖块数目,dr表示堆下一层时 所需要的右侧砖块数目)

image.png

image.png

(新颜色表示的砖块 代表 每堆高一层(下一层)所需要周围预先布置好的砖块数,左边为dl=1,右边为dr=1)

image.png

(左边为dl=2,右边为dr=2)

#对于特殊情况(边界情况):
1.单边:
对于左边或右边已经到达边界,为使index处的高度增加,每增加一层,不再需要该侧更多的砖块,保持dl或dr即可,此时只有某一侧的顶层,满足严格的三角形,而未到达边界的一侧继续推进。

image.png

(左边为dl=3,右边为dr=3)

image.png

(左边为dl=3,右边为dr=4)

2.双边:
当两边都已经达到边界,为使index处的高度继续增加,每增加一层,只需要固定数目的砖块数量即可,此时(对于每一列的顶层,已经满足严格的三角形),可以直接将剩余砖块瓜分**(否则会超时)**,其中index处分得砖块数量最多或至少与其他列相同,根据是否可以平均分配,index列可以增加的高度为 diff%n==0?diff/n:diff/n+1。

image.png

(左边为dl=3,右边为dr=5,之后 dl,dr值均保持不变)

class Solution {
    public int maxValue(int n, int index, int maxSum) {
      int diff=maxSum-n,left=index,right=index, res=1,dl=0,dr=0; 
      while(diff>0){                     //当还有剩余砖块时
        if(--left>=0) dl++;              //尚未到达左边界
        if(++right<n) dr++;              //尚未到达右边界
        if(left<0&&right>=n){            //当到达左边界和右边界时 及时退出 
          res+=diff%n==0?diff/n:diff/n+1; //把剩余的砖块均分了,直接退出
          return res;
        }
        res+=1;          //层数更新
        diff-=(dl+dr+1); //使顶层堆成严格正三角形所需的砖块数(左边所需+右边所需+index处1个)
      }      
      return res;
    }
}

作者:ivon_shi-a-feng-ya
链接:https://leetcode-cn.com/problems/maximum-value-at-a-given-index-in-a-bounded-array/solution/jian-dan-mo-ni-jiu-ceng-zhi-tai-qi-yu-le-c6f6/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值