1969.数组元素的最小非零乘积
【LetMeFly】1969.数组元素的最小非零乘积:贪心(快速幂)
力扣题目链接:https://leetcode.cn/problems/minimum-non-zero-product-of-the-array-elements/
给你一个正整数 p
。你有一个下标从 1 开始的数组 nums
,这个数组包含范围 [1, 2p - 1]
内所有整数的二进制形式(两端都 包含)。你可以进行以下操作 任意 次:
- 从
nums
中选择两个元素x
和y
。 - 选择
x
中的一位与y
对应位置的位交换。对应位置指的是两个整数 相同位置 的二进制位。
比方说,如果 x = 1101
且 y = 0011
,交换右边数起第 2
位后,我们得到 x = 1111
和 y = 0001
。
请你算出进行以上操作 任意次 以后,nums
能得到的 最小非零 乘积。将乘积对 109 + 7
取余 后返回。
注意:答案应为取余 之前 的最小值。
示例 1:
输入:p = 1 输出:1 解释:nums = [1] 。 只有一个元素,所以乘积为该元素。
示例 2:
输入:p = 2 输出:6 解释:nums = [01, 10, 11] 。 所有交换要么使乘积变为 0 ,要么乘积与初始乘积相同。 所以,数组乘积 1 * 2 * 3 = 6 已经是最小值。
示例 3:
输入:p = 3 输出:1512 解释:nums = [001, 010, 011, 100, 101, 110, 111] - 第一次操作中,我们交换第二个和第五个元素最左边的数位。 - 结果数组为 [001, 110, 011, 100, 001, 110, 111] 。 - 第二次操作中,我们交换第三个和第四个元素中间的数位。 - 结果数组为 [001, 110, 001, 110, 001, 110, 111] 。 数组乘积 1 * 6 * 1 * 6 * 1 * 6 * 7 = 1512 是最小乘积。
提示:
1 <= p <= 60
方法一:贪心(快速幂)
方法
解决这道题需要明白两点:
- 尽量把所有数变成
111...10
和000...01
的形式; - 如何使用
快速幂
快速计算。
原因
为什么要尽量变成111...10
和000...01
的形式?
题目中的两个数
x = 1100
和y = 0011
,x * y = 1100 * 0010 + 1100 * 0001
。我们看从左往右的第三个
1
,它的“贡献”是1100 * 0010
。而如果交换第三个
1
的位置使得x = 1110
且y = 0001
(x * y = 1000 * 0001 + 0100 * 0001 + 0010 * 0001
),则第三个
1
的“贡献”是0010 * 0001
。有没有发现,第三个
1
不论在x
中还是在y
中,其对x * y
的贡献中总有“0010
乘以另一个数
”这一部分。那么能不能让另一个数尽可能小?可以。因最终结果要“非零”,因此另一个数最小为
1
。
能变成多少个111...10
和000...01
的形式?
从
1
到2^p-1
的二进制数中,除了2^p-1
(二进制下全部为1
)外,其余数(偶数个)正好可以两两配对(使得每一位上总计1个1
和1个0
)。因此最终可以变成$2^p-1$乘以:$\frac{2^p-2}{2}$个$2^p-2$
快速幂是什么?
快速幂用于快速计算$a^b\mod p$的结果,其原理是将
b
看成二进制数。例如$a^6=a^{110_{(2)}}=a^{100_{(2)}}\times a^{10_{(2)}}$,而$a^{100_{(2)}}$又可以通过$a^{10_{(2)}}\times a$得到。
这样便将$O(b)$的时间复杂度将为了$b$二进制长度的复杂度$O(\log b)$
具体可参考快速幂小解。
时空复杂度
- 时间复杂度$O(p)$
- 空间复杂度$O(1)$
AC代码
C++
1 |
|
Python
1 |
|
同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/136872322