【Leetcode】1969. 数组元素的最小非零乘积

文章目录

题目

题目链接🔗

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

从 n u m s nums nums 中选择两个元素 x x x 和 y y y 。

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

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

请你算出进行以上操作 任意次 以后, n u m s nums nums 能得到的 最小非零 乘积。将乘积对 1 0 9 + 7 10^9 + 7 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, 1 10, 011, 100, 001, 110, 111] 。
  • 第二次操作中,我们交换第三个和第四个元素中间的数位。
  • 结果数组为 [001, 110, 00 1, 110, 001, 110, 111] 。

数组乘积 1 * 6 * 1 * 6 * 1 * 6 * 7 = 1512 是最小乘积。
提示:
1 ≤ p ≤ 60 1 \leq p \leq 60 1≤p≤60

思路

首先我们要明确一点:通过交换位操作,我们希望得到的是一个最小非零乘积。接下来我们来分析一下贪心策略:

对于两个数 x x x和 y y y,如果我们将 y y y中除了最低位以外的所有位都赋值为1,那么交换后的结果将会使得乘积变小且非零。

为什么会这样呢?假设 x x x的参与交换的位是 000 000 000, y y y的参与交换的位是 111 111 111,交换后的结果中, x x x将变成 x + 2 k x+2^k x+2k, y y y将变成 y ′ y' y′,此时, ( x + 2 k ) × y ′ = x × y ′ + y ′ × 2 k (x+2^k) \times y' = x \times y' + y' \times 2^k (x+2k)×y′=x×y′+y′×2k。对比之前的乘积,我们可以发现只要 y ′ < x y'<x y′<x,交换后的乘积就会变小。因此,我们可以不断地将 y y y中的 111 111 111与 x x x中的 000 000 000交换,使得 y y y变成 111 111 111,从而使得乘积变得最小。

基于这个思路,我们可以构造数组 n u m s nums nums,将 [ 1 , 2 p − 1 ] [1, 2^p - 1] [1,2p−1]中的所有整数分为两组:小于 2 p − 1 2^p - 1 2p−1的为一组 A A A,其余的为另一组 B B B。对于 B B B中的元素,除了 2 p − 1 2^p - 1 2p−1以外,其余的数都可以和 A A A中的数一一配对,使得每对的和为 2 p − 1 2^p - 1 2p−1。然后,我们对每一对进行交换操作,使得 B B B中的数变为 111 111 111。这样一来,我们得到了一个数组 n u m s nums nums,其中每一对的乘积都是 2 p − 2 2^p - 2 2p−2,最终的乘积就是 ( 2 p − 1 ) × ( 2 p − 2 ) 2 p − 1 − 1 (2^p - 1) \times (2^p - 2)^{2^{p-1} - 1} (2p−1)×(2p−2)2p−1−1。

代码

c++ 复制代码
class Solution {
    int mod = 1000000007;
    public: int minNonZeroProduct(int p) {
        long x = (1L << p) - 1;
        return (int) (myPow((x - 1) % mod, x >> 1) * (x % mod) % mod);
    }
    long myPow(long a, long b) {
        long res = 1;
        while (b > 0) {
            if ((b & 1) == 1) res = res * a % mod;
            a = a * a % mod;
            b >>= 1;
        }
        return res;
    }
};

复杂度分析

时间复杂度

构造数组 n u m s nums nums的时间复杂度为 O ( 2 p ) O(2^p) O(2p),其中 2 p 2^p 2p为数组中元素的个数。

快速幂算法在计算 ( 2 p − 2 ) 2 p − 1 − 1 (2^p - 2)^{2^{p-1} - 1} (2p−2)2p−1−1时的时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn),其中 n = 2 p − 2 n = 2^p - 2 n=2p−2。

因此,总的时间复杂度为 O ( 2 p + log ⁡ n ) O(2^p + \log n) O(2p+logn)。

空间复杂度

代码中只使用了常数额外的空间,因此空间复杂度为 O ( 1 ) O(1) O(1)。

结果

总结

通过贪心策略和数学推导,我们得到了一个简洁高效的解决方案。贪心策略是通过寻找最小非零乘积的最优交换方式,从而使得乘积变得最小。数学推导则是基于贪心策略得出了最终的乘积公式,通过快速幂算法高效地计算出结果。整体来说,这个解决方案在时间和空间上都表现出了较高的效率。

相关推荐
Echo``27 分钟前
1:OpenCV—图像基础
c++·图像处理·人工智能·opencv·算法·计算机视觉·视觉检测
COOCC11 小时前
激活函数全解析:定义、分类与 17 种常用函数详解
人工智能·深度学习·神经网络·算法·机器学习·计算机视觉·自然语言处理
林下清风~1 小时前
力扣hot100——347.前K个高频元素(cpp手撕堆)
算法·leetcode·职场和发展
小雅痞1 小时前
[Java][Leetcode middle] 238. 除自身以外数组的乘积
java·leetcode
进击的小白菜2 小时前
Java回溯算法解决非递减子序列问题(LeetCode 491)的深度解析
java·算法·leetcode
-一杯为品-3 小时前
【深度学习】#11 优化算法
人工智能·深度学习·算法
-qOVOp-4 小时前
zst-2001 上午题-历年真题 计算机网络(16个内容)
网络·计算机网络·算法
Swift社区4 小时前
涂色不踩雷:如何优雅解决 LeetCode 栅栏涂色问题
算法·leetcode·职场和发展
冠位观测者4 小时前
【Leetcode 每日一题】2900. 最长相邻不相等子序列 I
数据结构·算法·leetcode
努力写代码的熊大4 小时前
链表的中间结点数据结构oj题(力扣876)
数据结构·leetcode·链表