数组元素的最小非零乘积

数组元素的最小非零乘积

给你一个正整数 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 是最小乘积。

算法思路:
考虑a+b=c,则fmin(a*b)=(c-1)*1(其中a,b,c均为正整数);
对于本题的p,可以找到2(p-1)-1组a,b,使得a+b=(2p)-1,剩下还有一个落单的(2^p)-1;
对于每组a,b,令a=(2^p)-2,b=1,此时对于数组中的任意两个数的交换操作都不会使数组乘积变小,这时所有数的乘积便是最小值

于是我们有如下算法实现

#define MOD 1000000007
int f(int n){
    int i=1;
    int s=1;
    while(i<=n){
            s=s*2;
            i++;
    }
    return s-1;
}
long long f2(long long  s,int n){
    int i=1;
    int p=s;
  //  if(n>10000)n=1000;
    for(i;i<n;i++){
     //   printf("%d ",s);
        s=s*p%MOD;
    }

    return s;
}
int minNonZeroProduct(int p){
    if(p==1) return 1;
  //  if(p==22) return 0;
    int n0,n1;
    int n=f(p); 
    int r=1;

    n0=(n-1)/2;
    n1=n0;
      printf("%d ",n1);
    int v0=f(r);
    int v1=f(p)-v0;
    long long  sv0=1;
   
    long long sv1=f2(v1,n1);
   // printf("n1  v1  %d %d ",n1,v1);

  //   printf("n0,n1,r %d %d  %d",n0,n1,r);
//
 //   printf("  v0,v1,sv0,sv1 %d %d  %d %d ",v0,v1,sv0,sv1);

    return sv0*sv1*n%MOD;

}

但是单纯的算法实现并不能求解问题,时间开销不够,所以需要使用快速幂
下面是使用快速幂的算法进行求解的结果:

#define MOD 1000000007
int minNonZeroProduct(int p){
    long long ret=1;
    long long a=((1l<<p)-2)%MOD;
    for(int i=0;i<p-1;i++){
        ret=ret*a%MOD;
        a=a*a%MOD;
    }
    return ret*(((1l<<p)-1)%MOD)%MOD;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值