【BD算法题】长度为n的数组的所有乘积为正的连续子数组个数、所有乘积为负的连续子数组个数

2023-05-16

Problem

魔法师小树有n个魔法元素,他把它们排成一行,从左到右第i个魔法元素的能量值是一个非零整数 a i a_i ai。小树每次施展魔法的方式是挑选一段连续的非空的魔法元素,将它们的能量乘起来,得到的值就是这次魔法的总能量。如果能量大于零即为白魔法,否则为黑魔法。

现在想知道施展一个白魔法或黑魔法的方案数分别有多少?两个方案不同是指挑选的连续区间不同。
描述:
第一行有一个整数n(1<n<2*10^5),表示魔法元素的个数。
第二行有n个整数 a 1 , a 2 , a 3 , . . . , a n ( − 1 0 9 < = a i < = 1 0 9 ; a i ≠ 0 ) a_1, a_2, a_3, ..., a_n (-10^9<=a_i<=10^9; a_i \neq 0) a1,a2,a3,...,an(109<=ai<=109;ai=0) 代表魔法元素的能量值。
输出描述:
输出两个整数,分别表示施展一个白魔法和施展一个黑魔法的方案数。

示例输入:

5
5 -3 3 -1 1

示例输出:

7 8

题目简化

题目比较长,简单来说,就是求长度为n的数组的所有乘积为正的连续子数组个数、所有乘积为负的连续子数组个数,其中数组中不含0. 与动态规划的经典问题乘积最大子数组不同,本题要找出所有情况,最简单直接的想法是两层for循环,暴力检索。

暴力法

两层for循环,i 为左指针, j 为右指针。注意长度为 1 的子数组的处理。

def problem2(arr):
    n = len(arr)
    poss = 0
    neg = 0
    for i in range(n):
        tmp=arr[i]
        if tmp>0:
            poss+=1
        else:
            neg +=1
        for j in range(i+1,n):
            tmp*=arr[j]
            if tmp>0:
                poss+=1
            else:
                neg +=1
    return poss, neg

连续子数组个数总和为 1 + 2 + 3 + . . . + n = n ∗ ( n + 1 ) / 2 1+2+3+...+n = n*(n+1)/2 1+2+3+...+n=n(n+1)/2, 所以时间复杂度为 O ( n 2 ) O(n^2) O(n2).
考虑到当数组中元素很大时,乘法可能耗时,可以先遍历一遍,只保存数组中元素的符号,以替代原数组。
然而,此方法的复杂度依旧很高,在n很大时无法快速求解。

由位置 i 推断位置 j

示例 递推求解 乘积为正连续子数组个数,乘积为负的子数组个数可以由 n ( n + 1 ) / 2 − 乘积为正的子数组个数 n(n+1)/2 - 乘积为正的子数组个数 n(n+1)/2乘积为正的子数组个数 求出。

原数组符号+-+-+++---
累计负号个数0112222345
signs:1~i 累积符号1+0-1001111010
乘积为正连续子数组个数112471116182427

如果已知数组 a [ 1 : i ] a[1:i] a[1:i]的乘积为正连续子数组个数,要计算数组 a [ 1 : i + 1 ] a[1:i+1] a[1:i+1]的乘积为正连续子数组个数,可以拆分为
1、 a [ 1 : i ] a[1:i] a[1:i]的乘积为正连续子数组个数
2、 a [ 1 : i + 1 ] , a [ 2 : i + 1 ] , a [ 3 : i + 1 ] , . . . , a [ i : i + 1 ] , a [ i + 1 ] a[1:i+1], a[2:i+1], a[3:i+1] ,... , a[i:i+1], a[i+1] a[1:i+1],a[2:i+1],a[3:i+1],...,a[i:i+1],a[i+1]中乘积为正的个数。

要快速判断 a [ 1 : i + 1 ] , a [ 2 : i + 1 ] , a [ 3 : i + 1 ] , . . . , a [ i : i + 1 ] , a [ i + 1 ] a[1:i+1], a[2:i+1], a[3:i+1] ,... , a[i:i+1], a[i+1] a[1:i+1],a[2:i+1],a[3:i+1],...,a[i:i+1],a[i+1] 乘积的符号,可以考虑用一个数组signs存下 a [ 1 ] ∗ a [ 2 ] ∗ . . . ∗ a [ i ] = a [ i ] ! a[1]*a[2]*...*a[i] = a[i]! a[1]a[2]...a[i]=a[i]!的符号(只需要遍历一次数组即可得到 O ( n ) O(n) O(n))。 a [ j : i ] a[j: i] a[j:i]的累乘符号,可以由 a [ j ] ! a[j]! a[j]! a [ i ] ! a[i]! a[i]!的符号确定。因此,a[1:i+1], a[2:i+1], a[3:i+1] ,... , a[i:i+1], a[i+1]中乘积为正的个数,可以由累积符号确定:

1、如果 a [ i + 1 ] a[i+1] a[i+1]为正,比如由上表中4到7,增加的情况有: + − + − + +-+-+ +++ , − + − + -+-+ ++, + + + 三种,即signs[:i+1]中1的个数。
2、如果 a [ i + 1 ] a[i+1] a[i+1]为负,比如由上表中16到18,增加的情况有: + − + + + − +-+++- ++++, − + + + − -+++- +++ 两种, 即sign[:i] 中0的个数。

由此写出代码:

def problem2_spped2(arr):
    '''
    signs: arr[i]! 的符号, 1正,0负
    poss_list: 到位置i的所有 乘积为正的 连续子数组个数
    ones, zeros: 记录signs几个0,几个1
    '''
    n=len(arr)
    tmp_sign = arr[0]
    poss = int(arr[0]==(abs(arr[0])))
    poss_list=[poss]
    signs = [poss]
    ones = int(poss==1)
    zeros = int(poss!=1)
    
    for i in range(1,len(arr)):
        tmp_sign *= arr[i]
        if tmp_sign<0:
            signs.append(0)
            zeros+=1
        else:
            signs.append(1)
            ones+=1
        
        if signs[i]==0:
            poss += zeros-1
        else:
            poss+= ones
        poss_list.append(poss)
    return poss, (n*(n+1))//2-poss

测试

if __name__ =='__main__':
    import time
    import numpy as np
    
    arr=[1,-2,3,-4,5, 6, 7, -8,-9,-10]
    t1 = time.time()
    print(problem2(arr))
    t2 = time.time()
    print(t2-t1)
    print(problem2_spped2(arr))
    t3 = time.time()
    print(t3-t2)

    arr = np.random.randn(1,2*10**5).tolist()[0]
    t2 = time.time()
    print(problem2_spped2(arr))
    t3 = time.time()
    print(t3-t2)

可以看到,两种方法结果一直,优化后的方法在处理 n = 2 ∗ 1 0 5 n=2*10^5 n=2105 也只需要0.05秒。

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【BD算法题】长度为n的数组的所有乘积为正的连续子数组个数、所有乘积为负的连续子数组个数 的相关文章

  • DB2 的自增主键方式

    DB2 的自增主键方式 xff1a 1 not null generated by default as identity 不会自增长 一定要指定主键值 2 not null GENERATED ALWAYS AS IDENTITY 自增
  • “猿”?“媛”?

    我来自农村 xff0c 父辈告诉我读书是走出这里的唯一途径 xff0c 所以 xff0c 教育在我们家备受重视 高考那年家里发生了一场变故 xff0c 我亲爱的爷爷去世了 xff0c 高考 xff0c 我是一个人跟着学校的车去考试的 xff
  • bootstrap后台 uniform.default.css 使用checkbox 默认选不中问题

    昨天在实际操作中遇见了一个问题 input type 61 39 checkbox 39 设置ckecked 选不中 一直以为是js问题 后来F12看页面发现 是样式的掩盖 lt label gt lt input name 61 34 g
  • linux下安装ffmpeg 语音amr文件为MP3 包含各依赖

    最近安装ffmpeg 转换语音amr文件为MP3 xff0c 在网上查看了很多的版本 xff0c 都是要make 编译 xff0c 而且还有装各种依赖 xff0c 如MP3解码lame等 在官网找到已打包好的文件 xff0c 直接安装 ht
  • 新浪微博与微信公众号开发总结

    微信公众号开发总结 微信公众号开发者文档地址 xff1a https mp weixin qq com wiki t 61 resource res main amp id 61 mp1445241432 可根据文档开始微信者公众号开发 x
  • ROS学习笔记(三)

    元功能包 将plumbing pub sub plumbing server client plumbing param server关联在一起 http wiki ros org catkin package xml Metapackag
  • ELK-filebeat+logstash采集nginx日志

    ELK filebeat 43 logstash采集nginx日志 文章目录 ELK filebeat 43 logstash采集nginx日志 前言采集访问日志第一种方式 xff1a 修改nginx访问日志输出格式为json修改nginx
  • 【计算机系统】CPU是如何运行程序的

    一 CPU组成部分 寄存器 xff1a 存储CPU执行的指令的数据 xff0c CPU每次执行指令都会重新更新寄存器 程序计数器 PC xff1a 记录CPU即将执行的指令内存中的地址 逻辑控制单元 ALU xff1a CPU中负责逻辑计算
  • ubuntu如何分区

    1 swap交换分区 xff0c 一般为你机器内存的两倍 xff0c 少于这个容量 xff0c 系统无法进入休眠 实质是硬盘上的交换空间而非分区 xff0c 所以没有格式 xff0c 默认休眠将数据储存于此 可以取消 xff08 如不用sw
  • git-cola安装与使用

    64 git cola安装与使用 linux下可视化git工具git cola安装与使用 xff08 SSH方式 xff09 链接 https blog csdn net zyhse article details 108813116
  • 家庭百兆升级千兆全攻略

    近日电信把家庭宽带给自动升成了300M xff0c 但奈何家里硬件限制 xff0c 一直都无法享受超快的速度 于是乎 xff0c 只有撸起袖子自己干 xff0c 下面来看看我的踩坑之旅吧 材料准备 千兆光猫6类以上网线 xff0c 最好带屏
  • ubuntu18.04 使用USB串口调试

    1 环境ubuntu18 04 安装了minicom环境 如果没有安装 xff0c 在执行minicom命令时会提示安装 step1 查看连接串口 gt 执行 ls dev tty Tab按键 目标串口 ttyUSB0 ttyUSB1 这个
  • STL详解及常见面试题

    文章目录 一 STL的介绍二 空间配置器详解1 第一级配置器详解2 第二级空间配置器详解3 空间配置器存在的问题 三 各种容器的特点和适用情况四 各种容器的底层机制和常见面试题1 vector xff08 1 xff09 vector的底层
  • VR中的9轴传感器(重力加速度/陀螺仪/磁力计).md

    前言 传感器的调试过程 xff0c 一般根据原厂提供demo代码 xff0c 调试数据接口 xff0c 将数据流打通即可 xff0c 在VR中 xff0c 当带上头显设备 xff0c 运行应用时 xff0c 出现漂移 延迟 不回归问题 xf
  • 数字信号处理 --- 信号分解基础

    信号的分解 重剑无锋 xff0c 大巧不工 信号的分解方式很多 xff0c 大家最常用也最熟知的就是傅里叶变换了 xff0c 然而有很多非常基础的分解方式往往不为人所知 他们的目的都是以某种方法去完美的分解并重建 还原信号 xff0c 闲来
  • leetcode系列-字符串

    字符串 xff1a 总结篇 从字符串的定义到库函数的使用原则 xff0c 从各种反转到KMP算法 什么是字符串 字符串是若干字符组成的有限序列 xff0c 也可以理解为是一个字符数组 反转系列 344 反转字符串 编写一个函数 xff0c
  • CPU种类

    CISC 1 英特尔处理器 xff1a 奔腾 赛扬 酷睿 至强 其中奔腾和赛扬系列定位低端 xff0c 酷睿系列又细分为酷睿i3 i5 i7 分别代表中端 中高端 高端 至强系列主要应用为服务器处理器 奔腾双核 xff0c 赛扬双核 xff
  • “段错误 (核心已转储) ”一种可能原因及其解决方法

    终端在运行的时候总是出现 段错误 核心已转储 栈空间用来存储数组等数据 xff0c 那么段错误就应该是我存储的数组超过了它所在段的大小 xff0c 于是在的程序执行的过程中一到跟大数组相关的步骤就会出现段错误的提示 xff08 SIGSEG
  • 统一javaweb项目和mysql数据库时间UTC时间方法及原理

    统一javaweb项目和mysql数据库时间UTC时间方法及原理 文章目录 统一javaweb项目和mysql数据库时间UTC时间方法及原理 前言UTC时间与 GMT时间时间戳和时区 mysql时间字段解读4种日期类型比如datetime和
  • 【读书笔记】《视觉SLAM十四讲(高翔著)》 第11讲

    文章目录 1 安装gtsam2 程序编译3 程序运行4 用g2o viewer打开 g2o文件 本博客的内容是本章程序编译运行方法 xff0c 记录调通本章程序的过程 处理遇到报错的解决方法 本章程序的详细解析可参考robinhjwy的CS

随机推荐