前缀和题目:使数组互补的最少操作次数

题目

标题和出处

标题:使数组互补的最少操作次数

出处:1674. 使数组互补的最少操作次数

难度

7 级

题目描述

要求

给定一个长度为偶数 n \texttt{n} n 的整数数组 nums \texttt{nums} nums 和一个整数 limit \texttt{limit} limit。每一次操作,可以将 nums \texttt{nums} nums 中的任何整数替换为 1 \texttt{1} 1 limit \texttt{limit} limit 之间的另一个整数,包含 1 \texttt{1} 1 limit \texttt{limit} limit

如果对于所有下标 i \texttt{i} i(下标从 0 \texttt{0} 0 开始), nums[i]   +   nums[n   -   1   -   i] \texttt{nums[i] + nums[n - 1 - i]} nums[i] + nums[n - 1 - i] 都等于同一个数,则数组 nums \texttt{nums} nums互补的。例如,数组 [1,2,3,4] \texttt{[1,2,3,4]} [1,2,3,4] 是互补的,因为对于所有下标 i \texttt{i} i nums[i]   +   nums[n   -   1   -   i]   =   5 \texttt{nums[i] + nums[n - 1 - i] = 5} nums[i] + nums[n - 1 - i] = 5

返回使数组 nums \texttt{nums} nums 互补最少操作次数。

示例

示例 1:

输入: nums   =   [1,2,4,3],   limit   =   4 \texttt{nums = [1,2,4,3], limit = 4} nums = [1,2,4,3], limit = 4
输出: 1 \texttt{1} 1
解释:经过 1 \texttt{1} 1 次操作,可以将数组 nums 变成 [1,2,2,3] \texttt{[1,2,2,3]} [1,2,2,3]
nums[0]   +   nums[3]   =   1   +   3   =   4 \texttt{nums[0] + nums[3] = 1 + 3 = 4} nums[0] + nums[3] = 1 + 3 = 4
nums[1]   +   nums[2]   =   2   +   2   =   4 \texttt{nums[1] + nums[2] = 2 + 2 = 4} nums[1] + nums[2] = 2 + 2 = 4
nums[2]   +   nums[1]   =   2   +   2   =   4 \texttt{nums[2] + nums[1] = 2 + 2 = 4} nums[2] + nums[1] = 2 + 2 = 4
nums[3]   +   nums[0]   =   3   +   1   =   4 \texttt{nums[3] + nums[0] = 3 + 1 = 4} nums[3] + nums[0] = 3 + 1 = 4
对于每个 i \texttt{i} i nums[i]   +   nums[n-1-i]   =   4 \texttt{nums[i] + nums[n-1-i] = 4} nums[i] + nums[n-1-i] = 4,所以 nums \texttt{nums} nums 是互补的。

示例 2:

输入: nums   =   [1,2,2,1],   limit   =   2 \texttt{nums = [1,2,2,1], limit = 2} nums = [1,2,2,1], limit = 2
输出: 2 \texttt{2} 2
解释:经过 2 \texttt{2} 2 次操作,可以将数组 nums \texttt{nums} nums 变成 [2,2,2,2] \texttt{[2,2,2,2]} [2,2,2,2]。不能将任何数字变更为 3 \texttt{3} 3,因为 3 > limit \texttt{3} > \texttt{limit} 3>limit

示例 3:

输入: nums   =   [1,2,1,2],   limit   =   2 \texttt{nums = [1,2,1,2], limit = 2} nums = [1,2,1,2], limit = 2
输出: 0 \texttt{0} 0
解释: nums \texttt{nums} nums 已经是互补的。

数据范围

  • n = nums.length \texttt{n} = \texttt{nums.length} n=nums.length
  • 2 ≤ n ≤ 10 5 \texttt{2} \le \texttt{n} \le \texttt{10}^\texttt{5} 2n105
  • 1 ≤ nums[i] ≤ limit ≤ 10 5 \texttt{1} \le \texttt{nums[i]} \le \texttt{limit} \le \texttt{10}^\texttt{5} 1nums[i]limit105
  • n \texttt{n} n 是偶数

解法

思路和算法

当数组 nums \textit{nums} nums 互补时,对于所有符合 0 ≤ i < j < n 0 \le i < j < n 0i<j<n i + j = n − 1 i + j = n - 1 i+j=n1 的下标 i i i j j j nums [ i ] + nums [ j ] \textit{nums}[i] + \textit{nums}[j] nums[i]+nums[j] 都是相同的值。

以下将符合 0 ≤ i < j < n 0 \le i < j < n 0i<j<n i + j = n − 1 i + j = n - 1 i+j=n1 的下标 i i i 和下标 j j j 处的两个元素称为一对元素。

由于数组 nums \textit{nums} nums 中的每个整数可以替换成 [ 1 , limit ] [1, \textit{limit}] [1,limit] 范围中的任意整数,因此每一对元素的和的取值范围是 [ 2 , limit × 2 ] [2, \textit{limit} \times 2] [2,limit×2]。令 sumLimit = limit × 2 \textit{sumLimit} = \textit{limit} \times 2 sumLimit=limit×2,则每一对元素的和的取值范围是 [ 2 , sumLimit ] [2, \textit{sumLimit}] [2,sumLimit]

target \textit{target} target 表示数组 nums \textit{nums} nums 互补时的每一对元素的和,则 2 ≤ target ≤ sumLimit 2 \le \textit{target} \le \textit{sumLimit} 2targetsumLimit。对于一对元素 nums [ i ] \textit{nums}[i] nums[i] nums [ j ] \textit{nums}[j] nums[j],将初始时这一对元素的和记为 sum \textit{sum} sum,为了使这一对元素的和变成 target \textit{target} target,需要执行 0 0 0 次至 2 2 2 次操作。

  • 如果执行 0 0 0 次操作,则这一对元素的和一定是 sum \textit{sum} sum。当 target = sum \textit{target} = \textit{sum} target=sum 时,执行 0 0 0 次操作即可使这一对元素的和等于 target \textit{target} target

  • 如果执行 1 1 1 次操作,则将其中的较大元素替换成 1 1 1 之后可以得到元素和最小值 min ⁡ ( nums [ i ] , nums [ j ] ) + 1 \min(\textit{nums}[i], \textit{nums}[j]) + 1 min(nums[i],nums[j])+1,将其中的较小元素替换成 limit \textit{limit} limit 之后可以得到元素和最大值 max ⁡ ( nums [ i ] , nums [ j ] ) + limit \max(\textit{nums}[i], \textit{nums}[j]) + \textit{limit} max(nums[i],nums[j])+limit。当 min ⁡ ( nums [ i ] , nums [ j ] ) + 1 ≤ target ≤ max ⁡ ( nums [ i ] , nums [ j ] ) + limit \min(\textit{nums}[i], \textit{nums}[j]) + 1 \le \textit{target} \le \max(\textit{nums}[i], \textit{nums}[j]) + \textit{limit} min(nums[i],nums[j])+1targetmax(nums[i],nums[j])+limit 时,执行 1 1 1 次操作即可使这一对元素的和等于 target \textit{target} target

  • target < min ⁡ ( nums [ i ] , nums [ j ] ) + 1 \textit{target} < \min(\textit{nums}[i], \textit{nums}[j]) + 1 target<min(nums[i],nums[j])+1 target > max ⁡ ( nums [ i ] , nums [ j ] ) + limit \textit{target} > \max(\textit{nums}[i], \textit{nums}[j]) + \textit{limit} target>max(nums[i],nums[j])+limit 时,需要执行 2 2 2 次操作才能使这一对元素的和等于 target \textit{target} target

minSumOne = min ⁡ ( nums [ i ] , nums [ j ] ) + 1 \textit{minSumOne} = \min(\textit{nums}[i], \textit{nums}[j]) + 1 minSumOne=min(nums[i],nums[j])+1 maxSumOne = max ⁡ ( nums [ i ] , nums [ j ] ) + limit \textit{maxSumOne} = \max(\textit{nums}[i], \textit{nums}[j]) + \textit{limit} maxSumOne=max(nums[i],nums[j])+limit。根据上述分析可知, target \textit{target} target 在不同区间对应的一对元素的操作次数如下。

  • target \textit{target} target 位于区间 [ 2 , minSumOne − 1 ] [2, \textit{minSumOne} - 1] [2,minSumOne1] 时,操作次数是 2 2 2

  • target \textit{target} target 位于区间 [ minSumOne , sum − 1 ] [\textit{minSumOne}, \textit{sum} - 1] [minSumOne,sum1] 时,操作次数是 1 1 1

  • target = sum \textit{target} = \textit{sum} target=sum 时,操作次数是 0 0 0

  • target \textit{target} target 位于区间 [ sum + 1 , maxSumOne ] [\textit{sum} + 1, \textit{maxSumOne}] [sum+1,maxSumOne] 时,操作次数是 1 1 1

  • target \textit{target} target 位于区间 [ maxSumOne + 1 , sumLimit ] [\textit{maxSumOne} + 1, \textit{sumLimit}] [maxSumOne+1,sumLimit] 时,操作次数是 2 2 2

为了得到使数组互补的最少操作次数,需要对 2 ≤ target ≤ sumLimit 2 \le \textit{target} \le \textit{sumLimit} 2targetsumLimit 的每个 target \textit{target} target 计算操作次数。由于每一对元素的和的范围是 [ 2 , sumLimit ] [2, \textit{sumLimit}] [2,sumLimit],因此直接计算每个 target \textit{target} target 的操作次数的时间复杂度过高。为了降低时间复杂度,需要使用差分数组。

创建长度为 sumLimit + 1 \textit{sumLimit} + 1 sumLimit+1 的差分数组 diffs \textit{diffs} diffs,对于 1 ≤ k ≤ sumLimit 1 \le k \le \textit{sumLimit} 1ksumLimit diffs [ k ] \textit{diffs}[k] diffs[k] 表示 target = k \textit{target} = k target=k target = k − 1 \textit{target} = k - 1 target=k1 对应的操作次数之差。对于每一对元素 nums [ i ] \textit{nums}[i] nums[i] nums [ j ] \textit{nums}[j] nums[j],更新差分数组的做法如下。

  1. diffs [ minSumOne ] \textit{diffs}[\textit{minSumOne}] diffs[minSumOne] 的值减 1 1 1

  2. diffs [ sum ] \textit{diffs}[\textit{sum}] diffs[sum] 的值减 1 1 1

  3. 如果 sum < sumLimit \textit{sum} < \textit{sumLimit} sum<sumLimit,将 diffs [ sum + 1 ] \textit{diffs}[\textit{sum} + 1] diffs[sum+1] 的值加 1 1 1

  4. 如果 maxSumOne < sumLimit \textit{maxSumOne} < \textit{sumLimit} maxSumOne<sumLimit,将 diffs [ maxSumOne + 1 ] \textit{diffs}[\textit{maxSumOne} + 1] diffs[maxSumOne+1] 的值加 1 1 1

遍历数组 nums \textit{nums} nums 之后,遍历差分数组,计算每个元素和对应的操作次数,得到最少操作次数。具体做法是,将操作次数初始化为 n n n,表示数组中的所有元素都需要替换,对于 1 ≤ i ≤ sumLimit 1 \le i \le \textit{sumLimit} 1isumLimit,将 diffs [ i ] \textit{diffs}[i] diffs[i] 加到操作次数,即可得到元素和 i i i 对应的操作次数。遍历差分数组结束之后,即可得到最少操作次数。

证明

上述解法中,对于每一对元素分别考虑 5 5 5 个区间,按照区间从小到大的顺序,每个区间对应的操作次数依次是 2 2 2 1 1 1 0 0 0 1 1 1 2 2 2

如果每个区间都非空,即区间的长度大于等于 1 1 1,则上述解法可以得到正确的结果。需要证明:如果存在空区间,即区间的长度等于 0 0 0,上述解法仍可以得到正确的结果。

操作次数是 0 0 0 的区间一定包含一个数,因此不为空。对于其余 4 4 4 个区间,为了方便分析,假设 nums [ i ] ≤ nums [ j ] \textit{nums}[i] \le \textit{nums}[j] nums[i]nums[j],则每个变量的值如下: sumLimit = limit × 2 \textit{sumLimit} = \textit{limit} \times 2 sumLimit=limit×2 sum = nums [ i ] + nums [ j ] \textit{sum} = \textit{nums}[i] + \textit{nums}[j] sum=nums[i]+nums[j] minSumOne = nums [ i ] + 1 \textit{minSumOne} = \textit{nums}[i] + 1 minSumOne=nums[i]+1 maxSumOne = nums [ j ] + limit \textit{maxSumOne} = \textit{nums}[j] + \textit{limit} maxSumOne=nums[j]+limit。则其余 4 4 4 个区间的等价表示如下。

  • [ 2 , minSumOne − 1 ] = [ 2 , nums [ i ] ] [2, \textit{minSumOne} - 1] = [2, \textit{nums}[i]] [2,minSumOne1]=[2,nums[i]]

  • [ minSumOne , sum − 1 ] = [ nums [ i ] + 1 , nums [ i ] + nums [ j ] − 1 ] [\textit{minSumOne}, \textit{sum} - 1] = [\textit{nums}[i] + 1, \textit{nums}[i] + \textit{nums}[j] - 1] [minSumOne,sum1]=[nums[i]+1,nums[i]+nums[j]1]

  • [ sum + 1 , maxSumOne ] = [ nums [ i ] + nums [ j ] + 1 , nums [ j ] + limit ] [\textit{sum} + 1, \textit{maxSumOne}] = [\textit{nums}[i] + \textit{nums}[j] + 1, \textit{nums}[j] + \textit{limit}] [sum+1,maxSumOne]=[nums[i]+nums[j]+1,nums[j]+limit]

  • [ maxSumOne + 1 , sumLimit ] = [ nums [ j ] + limit + 1 , limit × 2 ] [\textit{maxSumOne} + 1, \textit{sumLimit}] = [\textit{nums}[j] + \textit{limit} + 1, \textit{limit} \times 2] [maxSumOne+1,sumLimit]=[nums[j]+limit+1,limit×2]

[ start , end ] [\textit{start}, \textit{end}] [start,end] 表示每个区间,则区间的长度是 max ⁡ ( 0 , end − start + 1 ) \max(0, \textit{end} - \textit{start} + 1) max(0,endstart+1)。由于 1 ≤ nums [ i ] ≤ nums [ j ] ≤ limit 1 \le \textit{nums}[i] \le \textit{nums}[j] \le \textit{limit} 1nums[i]nums[j]limit,因此这 4 4 4 个区间都满足 end ≥ start − 1 \textit{end} \ge \textit{start} - 1 endstart1,即当区间为空时一定有 end = start − 1 \textit{end} = \textit{start} - 1 end=start1

用区间 [ start , end ] [\textit{start}, \textit{end}] [start,end] 更新差分数组时,做法是将差分数组的下标 start \textit{start} start 处的元素加 1 1 1 并将下标 end + 1 \textit{end} + 1 end+1 处的元素减 1 1 1,或者将差分数组的下标 start \textit{start} start 处的元素减 1 1 1 并将下标 end + 1 \textit{end} + 1 end+1 处的元素加 1 1 1。当区间 [ start , end ] [\textit{start}, \textit{end}] [start,end] 为空时, end + 1 = start \textit{end} + 1 = \textit{start} end+1=start,因此更新差分数组的效果是两次更新抵消,不会更新差分数组的任何元素值。

因此,如果存在空区间,上述解法仍可以得到正确的结果。

代码

class Solution {
    public int minMoves(int[] nums, int limit) {
        int sumLimit = limit * 2;
        int[] diffs = new int[sumLimit + 1];
        int n = nums.length;
        for (int i = 0, j = n - 1; i < j; i++, j--) {
            int sum = nums[i] + nums[j];
            int minSumOne = Math.min(nums[i], nums[j]) + 1;
            int maxSumOne = Math.max(nums[i], nums[j]) + limit;
            diffs[minSumOne]--;
            diffs[sum]--;
            if (sum < sumLimit) {
                diffs[sum + 1]++;
            }
            if (maxSumOne < sumLimit) {
                diffs[maxSumOne + 1]++;
            }
        }
        int minimumMoves = n, moves = n;
        for (int i = 1; i <= sumLimit; i++) {
            moves += diffs[i];
            minimumMoves = Math.min(minimumMoves, moves);
        }
        return minimumMoves;
    }
}

复杂度分析

  • 时间复杂度: O ( n + limit ) O(n + \textit{limit}) O(n+limit),其中 n n n 是数组 nums \textit{nums} nums 的长度, limit \textit{limit} limit 是允许替换成的最大整数。需要创建长度为 limit × 2 + 1 \textit{limit} \times 2 + 1 limit×2+1 的差分数组,需要遍历数组 nums \textit{nums} nums 一次更新差分数组的值,需要遍历差分数组一次计算最少操作次数,因此时间复杂度是 O ( n + limit ) O(n + \textit{limit}) O(n+limit)

  • 空间复杂度: O ( limit ) O(\textit{limit}) O(limit),其中 limit \textit{limit} limit 是允许替换成的最大整数。需要创建长度为 limit × 2 + 1 \textit{limit} \times 2 + 1 limit×2+1 的差分数组。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

伟大的车尔尼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值