白话京东周赛第三题——将 x 减到 0 的最小操作数

题目来源5602. 将 x 减到 0 的最小操作数

一.题目

给你一个整数数组 n u m s nums nums和一个整数 x x x 。每一次操作时,你应当移除数组 n u m s nums nums 最左边或最右边的元素,然后从 x x x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。

如果可以将 x x x 恰好减到 0 0 0,返回最小操作数 ;否则,返回 − 1 -1 1

示例 1:

输入:nums = [1,1,4,2,3], x = 5
输出:2
解释:最佳解决方案是移除后两个元素,将 x 减到 0

示例 2:

输入:nums = [5,6,7,8,9], x = 4
输出:-1

示例 3:

输入:nums = [3,2,20,1,1,3], x = 10
输出:5
解释:最佳解决方案是移除后三个元素和前两个元素(总共 5 次操作),将 x 减到 0

提示:

  • 1 < = n u m s . l e n g t h < = 1 0 5 1 <= nums.length <= 10^5 1<=nums.length<=105
  • 1 < = n u m s [ i ] < = 1 0 4 1 <= nums[i] <= 10^4 1<=nums[i]<=104
  • 1 < = x < = 1 0 9 1 <= x <= 10^9 1<=x<=109

二.解题过程

2.1 思路分析

根据题意,我们需要在一个数组 n u m s nums nums中不断删除开头或结尾的元素,直到删去元素的和等于 x x x,而要完成这一目标,可能有很多种操作方法,我们需要寻找满足该条件最少的删除次数,举个栗子吧:

nums=[1,1,1,1,1,4,7,5,3,2],x=5

其操作方式可以为:

方式1.删除开头的51,即删除5次
方式2.删除末尾的32,即删除2次
方式3.删除开头的31和结尾的2,即删除4

其中方式2才是满足条件的最少删除次数。设原数组的元素和为 s u m sum sum,由于题目中限定了每次只能删除开头或结尾的元素,因此删除后留下来的是一个原数组的连续子序列,该子序列的值为 s u m − x sum - x sumx,其中 s u m sum sum为数组 n u m s nums nums的元素和。基于此,我们可以将原问题转化为求一个数组 n u m s nums nums中和为 s u m − x sum - x sumx的最长连续子序列,然后我们用数组的长度减去该最长子序列的长度就可以得到原题的答案。

2.2 算法介绍

接下来,简要介绍一下如何求一个数组 n u m s nums nums中和为 s u m − x sum -x sumx的最长连续子序列(划重点):

  • 首先,设待求满足条件的连续最长子序列的长度为 r e s = 0 res = 0 res=0,然后求出 n u m s nums nums各个元素为结尾元素的子序列的和数组 t o t a l total total,即:

    #n为数组长度
    total[0] = nums[0]
    total[1] = nums[0] + nums[1]
    ...
    total[n] = nums[0] + nums[1] + ... + nums[n]
    
  • 然后,我们从头开始遍历数组 n u m s nums nums,当遍历到位置 i ( 0 ⩽ i ⩽ n ) i(0 \leqslant i \leqslant n) i(0in)时,需要寻找是否有 i i i为结尾元素的子序列 n u m s [ j . . i ] nums[j..i] nums[j..i],其满足:
    ∑ m = j i n u m s [ m ] = s u m − x \sum_{m=j}^i nums[m]=sum-x m=jinums[m]=sumx
    若存在则将此时的序列长 i − j i-j ij与之前记录的最长子序列 r e s res res进行比较,若 i − j > r e s i-j > res ij>res,则 r e s = i − j res = i - j res=ij

    注:子序列 n u m s [ j . . i ] nums[j..i] nums[j..i]的和通过 t o t a l [ i ] total[i] total[i]减去 t o t a l [ j − 1 ] total[j-1] total[j1]来获得

  • 最后,用数组 n u m s nums nums的长度减去该最长子序列的长度,即为满足原提议的最小操作次数。

三.示例代码

这里给出该算法的python实现代码,需要注意的是我并没有真正去实现一个 t o t a l total total数组(后面为了表述方便继续使用这个概念),而是通过哈希表(通过字典 < k e y , v a l u e > <key,value> <key,value>的形式)来存储数组 n u m s nums nums中各个元素对应的 t o t a l [ i ] total[i] total[i]以及元素对应的位置 i i i,即 k e y = t o t a l [ i ] , v a l u e = i key = total[i],value = i key=total[i]value=i。通过这种方式,我们可以快速查询到是否有以 n u m s [ i ] nums[i] nums[i]为结束元的子序列,满足其和等于 s u m − x sum - x sumx,方法是寻找是否存在键为 t o t a l [ i ] − ( s u m − x ) total[i] - (sum -x ) total[i](sumx)的元素,这样以来就可以避免了双重循环(肯定超时)。

from typing import List

class Solution:
    def minOperations(self, nums: List[int], x: int) -> int:
        #初始化满足条件的最长子序列长res为0
        s,res = 0,0
        #计算sum -x 
        k = sum(nums) - x
        #初始化字典
        map = {}
        map[0]=-1
        for i in range(len(nums)):
            #累加计算total[i]
            s += nums[i]
            #寻找是否存在键为total[i] - (sum -x)的元素
            if map.get(s - k) != None:
                res = max(i - map.get(s-k),res)
            #存储<total[i],i>
            if map.get(s) == None:
                map[s] = i
        #排除nums数组和为x的情况,这种情况下,res的值也为0
        if sum(nums) == x:
            return len(nums)
        return len(nums) - res if res != 0 else -1

运行结果截图如下:
运行结果
以上便是本文的全部内容,若是觉得不错话可以点个赞或关注一下博主吧,你们的支持是博主继续创建的不竭动力!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

斯曦巍峨

码文不易,有条件的可以支持一下

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

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

打赏作者

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

抵扣说明:

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

余额充值