1664 生成平衡数组的方案数(动态规划、分析)

1. 问题描述:

给你一个整数数组 nums 。你需要选择恰好一个下标(下标从 0 开始)并删除对应的元素。请注意剩下元素的下标可能会因为删除操作而发生改变。比方说,如果 nums = [6,1,7,4,1] ,那么:

  • 选择删除下标 1 ,剩下的数组为 nums = [6,7,4,1] 。
  • 选择删除下标 2 ,剩下的数组为 nums = [6,1,4,1] 。
  • 选择删除下标 4 ,剩下的数组为 nums = [6,1,7,4] 。

如果一个数组满足奇数下标元素的和与偶数下标元素的和相等,该数组就是一个 平衡数组 。请你返回删除操作后,剩下的数组 nums 是平衡数组的方案数 。

示例 1:

输入:nums = [2,1,6,4]
输出:1
解释:
删除下标 0 :[1,6,4] -> 偶数元素下标为:1 + 4 = 5 。奇数元素下标为:6 。不平衡。
删除下标 1 :[2,6,4] -> 偶数元素下标为:2 + 4 = 6 。奇数元素下标为:6 。平衡。
删除下标 2 :[2,1,4] -> 偶数元素下标为:2 + 4 = 6 。奇数元素下标为:1 。不平衡。
删除下标 3 :[2,1,6] -> 偶数元素下标为:2 + 6 = 8 。奇数元素下标为:1 。不平衡。
只有一种让剩余数组成为平衡数组的方案。

示例 2:

输入:nums = [1,1,1]
输出:3
解释:你可以删除任意元素,剩余数组都是平衡数组。

示例 3:

输入:nums = [1,2,3]
输出:0
解释:不管删除哪个元素,剩下数组都不是平衡数组。

提示:

  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^4

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ways-to-make-a-fair-array

2. 思路分析:

① 首先我们需要根据题目的描述进行分析,一开始的时候可能没有什么思路但是当你写出几个具体的例子会发现其实就是模拟整个过程找到满足题目限制条件的答案而已。由题目可以知道我们需要判断出删除当前的nums[i]元素之后重新排列数组中的数字,判断奇数下标与偶数下标的元素和是否相等,如果相等那么当前的方案数是加1的,并且删除当前的元素nums[i]之后可以发现nums[i]前面的下标的奇偶性是不变的,但是后面的数字重新排列之后奇偶性会发生改变,也就是奇偶性会相反,所以一个比较容易想到的做法是先遍历一遍nums数组,使用两个变量sum_even,sum_odd来存储奇数下标与偶数下标对应的和,然后在另外一个循环中尝试删除当前的位置i对应的元素,并且我们需要在循环中记录到达当前位置i的奇数下标与偶数下标对应的和:left_even, left_odd,目的是为了计算左边奇数下标与偶数下标的和,这样尝试删除当前的nums[i]的时候就可以使用sum_even,sum_odd分别减去左边的left_even, left_odd得到右边奇数下标与偶数下标对应的和,计算出当前位置左右两边的奇数下标与偶数下标对应元素和之后那么就可以使用left_even + right_odd == left_odd + right_even判断是否满足题目条件了(删除当前元素之后删除位置之后的奇偶性会发生改变)

② 除了①比较容易想到的方法之外,看了一下力扣的题解,发现其中使用动态规划解决的思路也是非常ok的,可以学习学习,下面是我的理解:
由①分析可以知道我们删除当前的nums[i]之后后面元素的奇偶性会发生变化,基于这一点可以想到使用记录奇数与偶数下标对应的差值,当尝试删除nums[i]的时候如果i位置之前的奇偶下标的差值与i之后奇偶下标的差值是一样的说明就是符合条件的,下面举出一个例子:nums = [2,1,6,5,1]当尝试删除位第二个位置的元素1的时候左边的奇偶下标的差值为2,右边为6 - 5 + 1 = 2两者是相等的说明最后重新排列之后奇偶下标的和是相等的:2 + 5 = 6 + 1,这一点也很好理解,因为删除nums[i]之后奇偶性发生相反变化,所以只有当i位置之前的奇偶下标差值与i位置之后的奇偶下标差值相等的时候,两者相减等于0才表示奇数与偶数下标的和是相等的,题解网址

3. 代码如下:

from typing import List


class Solution:
    def waysToMakeFair(self, nums: List[int]) -> int:
        sum_even, sum_odd = 0, 0
        for i in range(len(nums)):
            if i % 2 == 0:
                sum_even += nums[i]
            else:
                sum_odd += nums[i]
        left_even, left_odd = 0, 0
        right_even, right_odd = 0, 0
        res = 0
        for i in range(len(nums)):
            # 尝试删除当前的下标i对应的元素
            if i % 2 == 0:
                right_even = sum_even - nums[i] - left_even
                right_odd = sum_odd - left_odd
                left_even += nums[i]
            else:
                right_even = sum_even - left_even
                right_odd = sum_odd - left_odd - nums[i]
                left_odd += nums[i]
            # 因为删除当前下标i的时候left_even或者是left_odd会多加一个nums[i]
            # 所以我们减去nums[i]而且需要修改的是对一下循环没有影响的变量所以只能够修改右边的奇 数或者是偶数下标对应的和
            if i % 2 == 0:
                right_odd -= nums[i]
            else:
                right_even -= nums[i]
            if left_even + right_odd == left_odd + right_even: res += 1
        return res
from typing import List


class Solution:
    def waysToMakeFair(self, nums: List[int]) -> int:
        n = len(nums)
        dp = [0] * (n + 1)
        for i in range(1, n + 1):
            dp[i] = dp[i - 1] + (nums[i - 1] if i % 2 else -nums[i - 1])
        ans = 0
        for i in range(1, n + 1):
            if dp[i - 1] == dp[n] - dp[i]:
                ans += 1
        return ans

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值