【每日一题】数组元素的最小非零乘积

Tag

【贪心】【快速幂】【数组】【2024-03-20】


题目来源

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


解题思路

方法一:贪心

前言

我们首先来思考一个简单的问题:假设给定三个整数 a a a b b b c c c,满足 1 < = a < b < c 1 <=a < b <c 1<=a<b<c,此时需要将某个数缩小 1,另外一个数增加 1,使得 abc 最小,如恶化选择才能最优?

  • 缩小时 优先缩小最小 的元素:当选择 a a a 缩小 1,此时三者乘积为 ( a − 1 ) b c (a-1)bc (a1)bc;当选择 b b b 缩小 1,此时三者乘积为 a ( b − 1 ) c a(b-1)c a(b1)c;当选择 c c c 缩小 1,此时三者乘积为 a b ( c − 1 ) ab(c-1) ab(c1)。比较三者有 ( a − 1 ) b c < a ( b − 1 ) c < a b ( c − 1 ) (a-1)bc < a(b-1)c < ab(c-1) (a1)bc<a(b1)c<ab(c1),所以为了使乘积最小,选择缩小最小的元素。
  • 增加时 优先增加最大 的元素:此时已经选择了缩小 a a a。当选择 b b b 增加 1,此时三者乘积为 ( a − 1 ) ( b + 1 ) c (a-1)(b+1)c (a1)(b+1)c;当选择 c c c 增加 1,此时三者乘积为 ( a − 1 ) b ( c + 1 ) (a-1)b(c+1) (a1)b(c+1)。比较二者有 ( a − 1 ) ( b + 1 ) c ≤ ( a − 1 ) b ( c + 1 ) (a-1)(b+1)c \le (a-1)b(c+1) (a1)(b+1)c(a1)b(c+1),所以为了使乘积最小,选择增加最大的元素。

位交换的本质

在本题中,将两个数对应的二进制数相同的位进行交换,本质上是将一个数缩小 2 k 2^k 2k,另一个数增加 2 k 2^k 2k,其中 k 是交换的第几位(二进制数从低位向高位方向数,从 0 开始数)。可以自行举例子加以理解。

示例分析

根据以上分析,我们可以知道一种贪心思路:进行相同位交换时,优先缩小数组中的自小元素,再增加数组中的最大元素。

设当前数组位 [ 1 , 2 , 3 , . . . , 2 p − 2 , 2 p − 1 ] [1, 2, 3, ..., 2^p-2, 2^p-1] [1,2,3,...,2p2,2p1],缩小时按照从小到大考虑每个元素:

  • 首先考虑缩小 1,此时需要将 1 减少 1,此时从右向左依次考虑最大的元素,由于 2 p − 1 2p−1 2p1 每一位均为 1 无法再增加;接着考虑 2 p − 2 2^p-2 2p2,此时将最低位变为 1,数组变为: [ 0 , 2 , 3 , ⋯   , 2 p − 3 , 2 p − 1 , 2 p − 1 ] [0, 2, 3, ⋯ , 2p−3, 2p−1, 2p−1] [0,2,3,,2p3,2p1,2p1]
  • 接着考虑缩小 2,此时需要将 2 减少 2,此时从右向左依次考虑最大的元素,由于 2 p − 1 2p−1 2p1 每一位均为 1 无法再增加;接着考虑 2 p − 3 2^p-3 2p3,此时将第 2 位变为 1,数组变为: [ 0 , 2 , 3 , ⋯   , 2 p − 1 , 2 p − 1 , 2 p − 1 ] [0, 2, 3, ⋯ , 2p−1, 2p−1, 2p−1] [0,2,3,,2p1,2p1,2p1]
  • 然后接着考虑 3 , 4 , 5 , ⋯ 3,4,5,⋯ 3,4,5,,按照上述最优变换原则,数组最终变换为 [ 0 , 0 , 0 , ⋯   , 2 p − 1 , ⋯   , 2 p − 1 ] [0,0,0,⋯ ,2p−1,⋯ ,2p−1] [0,0,0,,2p1,,2p1],由于最小乘积不能为 0,所有元素均不能为 0,此时则需要从所有 2 p − 1 2p−1 2p1 中移除最低位的 1 补充到 0 上,此时数组最终变换为 [ 1 , 1 , 1 , ⋯   , 1 ⏟ 2 p − 1 − 1 , 2 p − 2 , ⋯   , 2 p − 2 ⏟ 2 p − 1 − 1 , 2 p − 1 ] [\underbrace{1,1,1,\cdots,1}_{2^{p-1}-1},\underbrace{2^p-2,\cdots,2^p-2}_{2^{p-1}-1},2^p-1] [2p11 1,1,1,,1,2p11 2p2,,2p2,2p1],最终的返回值即为 ( 2 p − 1 ) × ( 2 p − 2 ) 2 p − 1 − 1 (2^p-1)\times(2^p-2)^{2^{p-1}-1} (2p1)×(2p2)2p11

以上内容主要参考 官方题解

快速幂

关于如何快速计算幂指数,可以参考 【面试经典150 | 数学】Pow(x, n)

实现代码

#define M 1000000007
#define ll long long
class Solution {
public:
    ll ExpMod(ll a, ll b, ll m){
        if(b == 0){
            return 1;
        }

        a %= M;
        ll tmp = ExpMod(a * a % m, b >> 1, m);
        if(b & 1){
            tmp = tmp * a % m;
        }
        return tmp;
    }

    int minNonZeroProduct(int p) {
        ll p2 = (ll)1 << p;

        return ExpMod(p2-2, p2/2-1, M) * ((p2-1) % M) % M; 
    }
};

复杂度分析

时间复杂度: O ( p ) O(p) O(p) p p p 表示给定的数。

空间复杂度: O ( 1 ) O(1) O(1)


写在最后

如果您发现文章有任何错误或者对文章有任何疑问,欢迎私信博主或者在评论区指出 💬💬💬。

如果大家有更优的时间、空间复杂度的方法,欢迎评论区交流。

最后,感谢您的阅读,如果有所收获的话可以给我点一个 👍 哦。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

wang_nn

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值