当前位置: 首页 > news >正文

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

文章目录

  • 题目
    • 标题和出处
    • 难度
    • 题目描述
      • 要求
      • 示例
      • 数据范围
  • 解法
    • 思路和算法
    • 证明
    • 代码
    • 复杂度分析

题目

标题和出处

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

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

难度

7 级

题目描述

要求

给定一个长度为偶数 n\texttt{n}n 的整数数组 nums\texttt{nums}nums 和一个整数 limit\texttt{limit}limit。每一次操作,可以将 nums\texttt{nums}nums 中的任何整数替换为 1\texttt{1}1limit\texttt{limit}limit 之间的另一个整数,包含 1\texttt{1}1limit\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}inums[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}inums[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≤105\texttt{2} \le \texttt{n} \le \texttt{10}^\texttt{5}2n105
  • 1≤nums[i]≤limit≤105\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<n0 \le i < j < n0i<j<ni+j=n−1i + j = n - 1i+j=n1 的下标 iiijjjnums[i]+nums[j]\textit{nums}[i] + \textit{nums}[j]nums[i]+nums[j] 都是相同的值。

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

由于数组 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 2sumLimit=limit×2,则每一对元素的和的取值范围是 [2,sumLimit][2, \textit{sumLimit}][2,sumLimit]

target\textit{target}target 表示数组 nums\textit{nums}nums 互补时的每一对元素的和,则 2≤target≤sumLimit2 \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,需要执行 000 次至 222 次操作。

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

  • 如果执行 111 次操作,则将其中的较大元素替换成 111 之后可以得到元素和最小值 min⁡(nums[i],nums[j])+1\min(\textit{nums}[i], \textit{nums}[j]) + 1min(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 时,执行 111 次操作即可使这一对元素的和等于 target\textit{target}target

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

minSumOne=min⁡(nums[i],nums[j])+1\textit{minSumOne} = \min(\textit{nums}[i], \textit{nums}[j]) + 1minSumOne=min(nums[i],nums[j])+1maxSumOne=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] 时,操作次数是 222

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

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

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

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

为了得到使数组互补的最少操作次数,需要对 2≤target≤sumLimit2 \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} + 1sumLimit+1 的差分数组 diffs\textit{diffs}diffs,对于 1≤k≤sumLimit1 \le k \le \textit{sumLimit}1ksumLimitdiffs[k]\textit{diffs}[k]diffs[k] 表示 target=k\textit{target} = ktarget=ktarget=k−1\textit{target} = k - 1target=k1 对应的操作次数之差。对于每一对元素 nums[i]\textit{nums}[i]nums[i]nums[j]\textit{nums}[j]nums[j],更新差分数组的做法如下。

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

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

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

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

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

证明

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

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

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

  • [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]≤limit1 \le \textit{nums}[i] \le \textit{nums}[j] \le \textit{limit}1nums[i]nums[j]limit,因此这 444 个区间都满足 end≥start−1\textit{end} \ge \textit{start} - 1endstart1,即当区间为空时一定有 end=start−1\textit{end} = \textit{start} - 1end=start1

用区间 [start,end][\textit{start}, \textit{end}][start,end] 更新差分数组时,做法是将差分数组的下标 start\textit{start}start 处的元素加 111 并将下标 end+1\textit{end} + 1end+1 处的元素减 111,或者将差分数组的下标 start\textit{start}start 处的元素减 111 并将下标 end+1\textit{end} + 1end+1 处的元素加 111。当区间 [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),其中 nnn 是数组 nums\textit{nums}nums 的长度,limit\textit{limit}limit 是允许替换成的最大整数。需要创建长度为 limit×2+1\textit{limit} \times 2 + 1limit×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 + 1limit×2+1 的差分数组。

http://www.dtcms.com/a/279452.html

相关文章:

  • 闲庭信步使用图像验证平台加速FPGA的开发:第十四课——图像二值化的FPGA实现
  • 如何集成光栅传感器到FPGA+ARM系统中?
  • JVM 内存模型详解:GC 是如何拯救内存世界的?
  • Oracle Virtualbox 虚拟机配置静态IP
  • 《亿级流量系统架构设计与实战》通用高并发架构设计 读场景
  • 1. 深入理解ArrayList源码
  • ae如何安装在非C盘
  • 7.15 窗口函数 | 二分 | 位运算
  • 逻辑代数中的基本规则,代入规则和反演规则,对偶规则
  • LLM notes
  • GitCode 使用高频问题及解决方案
  • TextIn:大学生的文档全能助手,让学习效率飙升
  • 【Linux庖丁解牛】— 信号的产生!
  • SwiftUI 常用控件分类与使用指南
  • SCI特刊征稿
  • 延迟双删懂不
  • .net swagger的API项目里面 同时可以运行wwwroot里面的网页
  • Java 中的异步编程详解
  • Desktop Extensions (DXT) 详解
  • CA翻译~
  • 12.如何判断字符串是否为空?
  • 153、寻找旋转排序数组中的最小值
  • 本地线程(Native Thread)、主线程(UI 线程) 和 子线程(Java 子线程)
  • Axure RP Extension for Chrome插件安装使用
  • 在 Ubuntu 上安装 vLLM:从 GPU 到 CPU 的三种方案
  • Oracle根据一张表的字段更新另一张表中的数据
  • Android 自定义路由系统
  • ServiceLibrary 库使用演示
  • [AI8051U入门第一步]环境安装和按键控制流水灯
  • 将dist文件打包成exe可执行程序