初学python记录:力扣1969. 数组元素的最小非零乘积

题目:

给你一个正整数 p 。你有一个下标从 1 开始的数组 nums ,这个数组包含范围 [1, 2^p - 1] 内所有整数的二进制形式(两端都 包含)。你可以进行以下操作 任意 次:

  • 从 nums 中选择两个元素 x 和 y  。
  • 选择 x 中的一位与 y 对应位置的位交换。对应位置指的是两个整数 相同位置 的二进制位。

比方说,如果 x = 1101 且 y = 0011 ,交换右边数起第 2 位后,我们得到 x = 1111 和 y = 0001 。

请你算出进行以上操作 任意次 以后,nums 能得到的 最小非零 乘积。将乘积对 109 + 7 取余 后返回。

注意:“最小值应为取余 之前 的最小值。

思考:

“选择 x 中的一位与 y 对应位置的位交换” ---->  (只有对应位置的位一个是0,一个是1的情况下,交换才有意义)所以题意即x减去2^k,y加上2^k

“可以进行交换操作任意次,使得数组内所有元素的乘积最小(不为0)” ----> 对于若干个数,每次取两个数,进行一个数减去2^k,另一个数加上2^k的操作,直到所有数相乘最小 ----> ?

数学分析:

1. 对于若干个数,如何选择交换的两个数?(即选择哪个数加,选择哪个数减?)

以a,b(a<b)两个数,分别加/减1为例:

原乘积=ab,选一个数加1后:情况一,乘积=(a+1)b=原乘积+b;情况二,乘积=(b+1)a=原乘积+a,显然,情况二乘积更小。选一个数减1后:情况一,乘积=(a-1)b=原乘积-b;情况二,乘积=(b-1)a=原乘积-a,显然,情况一乘积更小。---->  选择更小的数减一,选择更大的数加一

为了验证这一推论,再以a,b,c(a<b<c)三个数,选两个数分别加/减1为例:

原乘积=abc,选一个数加1后:情况一,乘积=(a+1)bc=原乘积+bc;情况二,乘积=(b+1)ac=原乘积+ac;情况三,乘积=(c+1)ab=原乘积+ab,显然,情况三乘积最小。选一个数减1后:情况一,乘积=(a-1)bc=原乘积-bc;情况二,乘积=(b-1)ac=原乘积-ac;情况三,乘积=(c-1)ab=原乘积-ab,显然,情况一乘积最小。---->  选择最小的数减一,选择最大的数加一,推论得到验证

2. 加/减多少才能使总乘积最小?(即加/减n,n应该取值多少?)

还是以a,b,c(a<b<c)三个数,选两个数分别加/减n为例:

原乘积=abc,选择最小数a减n,最大数c加n,乘积

=(a-n)b(c+n)

=abc+abn-nbc-n²

=abc+n[b(a-c)-n],

(a-c) < 0  ---->  n[b(a-c)-n] < 0  ---->  abc+n[b(a-c)-n] < abc

所以,只要进行加/减n(n>0)操作,乘积一定会变小,直至有数变到0为止。也就是说,n在满足a-n>0的条件下尽可能取大的值,所以n=a-1

3.综合1和2,分析数组[1,2,3,......,2^p-3,2^p-2,2^p-1]

数组一共有2^p-1个元素,即奇数个元素,那么按照常规思路两两交换后,会剩下中间一个数无法进行交换操作,这样得到的乘积也不是最小的。

那如何将考虑对象变成偶数个元素呢?

2^p-1的二进制全为1,因此没有为0的位可供交换,所以将最后一个元素2^p-1刨除不看,以1和2^p-2对应,2和2^p-3对应......的关系,两两对应并进行互换操作(即加/减2^k)。

本来因为乘积不能为0,是不能将最小数减到0的,但是为了方便计算,我们将最小数都先减到0,然后对所有0进行+1操作,对所有最大值进行-1操作。

对于1和2^p-2:数组变为[0,2,3,......,2^p-3,2^p-1,2^p-1]

对于2和2^p-3:数组变为[0,0,3,......,2^p-1,2^p-1,2^p-1]

......

到最后,数组变为[0,0,0,......,2^p-1,2^p-1,2^p-1],其中0有2^(p-1)-1个,2^p-1有2^(p-1)个

再对所有0进行+1操作,对所有最大值进行-1操作,原来刨除的最后一个数不变,数组变为[1,1,1,......,2^p-2,2^p-2,2^p-1],其中1有2^(p-1)-1个,2^p-2有2^(p-1)-1个,2^p-1有1个

此时计算数组的所有元素的乘积=1* (2^p-2) ^ (2^(p-1)-1) * (2^p-1)

代码如下:

class Solution(object):
    def minNonZeroProduct(self, p):
        """
        :type p: int
        :rtype: int
        """
        if p ==1:
            return 1
        mod = 10**9 + 7
        answer = (2**p-2)**(2**(p-1)-1) * (2**p-1) %mod
        
        return answer

测试通过,但提交发现超时了 :

查看题解之后懂了,因为幂数太大了,所以用这个代码求幂太慢,得用快速求幂,python的快速求幂函数如下:

base = 2
exponent = 3
result = pow(base, exponent)   # base是底数,exponent是指数
print(result)  # 输出: 8

修改原代码如下:

class Solution(object):
    def minNonZeroProduct(self, p):
        """
        :type p: int
        :rtype: int
        """
        if p ==1:
            return 1
        mod = 10**9 + 7
        base = 2**p-2
        exponent = (2**(p-1)-1)
        answer = pow(base, exponent, mod) * (2**p-1) %mod

        return answer

提交通过:

 

 

  • 21
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值