Leetcode 3130. Find All Possible Stable Binary Arrays II

  • Leetcode 3130. Find All Possible Stable Binary Arrays II
    • 0. 序言
    • 1. 算法思路
    • 2. 代码实现
      • 1. 第一版本
      • 2. 第二版本
      • 3. 第三版本
      • 4. 第四版本
    • 3. 算法优化
      • 1. 算法实现一
      • 2. 算法实现二
  • 题目链接:3130. Find All Possible Stable Binary Arrays II

0. 序言

这道题和题目3129本质上就是一道题目,唯一的差别就是取值范围的差异,题目3129的范围在 [ 1 , 200 ] [1,200] [1,200],而这道题在 [ 1 , 1000 ] [1, 1000] [1,1000],因此复杂度会更高。

很不幸,我只搞定了题目3129,而这道题则是死活都没有搞定,最后是看了一下题目3129当中其他大佬们的高效率算法之后改写了一下通过了最后的测试,不过不幸的是,具体到思路上依然是没有看懂,真的是有点伤……

所以,这里的话我会现在前两部分讲一下我自己的算法思路,以及为了提升算法效率而做的优化,总体来说的话,在题目3129当中将算法效率提升了10倍左右,耗时从3727ms降至574ms,不过不幸的是依然无法通过题目3130的测试样例……

因此,如果有读者对这部分内容不感兴趣的话可以直接跳到第三部分看一下大佬们的高效算法即可。

1. 算法思路

这一题我整体的算法思路是当做一个数学上的排列组合问题来做的,显然,如果0和1的个数 n , m ≤ l i m i t n, m \leq limit n,mlimit的话,那么显然我们可以直接给出答案 C n + m n C_{n+m}^{n} Cn+mn

但问题就在于如果有 n , m > l i m i t n, m > limit n,m>limit的情况,此时就不能直接用数学方法解了,本来考虑如果将 l i m i t + 1 limit+1 limit+1(不妨简记 l i m i t + 1 = k limit+1=k limit+1=k)个元素进行绑定然后填充的方式,倒是可以直接计算组合数为: C n + m − k n ⋅ ( n + 1 ) C_{n+m-k}^{n} \cdot (n+1) Cn+mkn(n+1)

不过这种情况仅限于 n < k n < k n<k k < m < 2 k k < m < 2k k<m<2k的情况,即要确保不可能存在两个组内的元素均不少于 k k k个,否则就会出现重复计数的情况。

最后,关于其他的情况,我们就是能用动态规划进行暴力求解了,就很繁琐。

2. 代码实现

1. 第一版本

首先,我们给出我们的第一版本的代码实现如下:

MOD = 10**9+7
FACTORIALS = [1 for _ in range(401)]
for i in range(1, 401):
    FACTORIALS[i] = i * FACTORIALS[i-1] % MOD
    
def rev(x):
    return pow(x, -1, MOD)

def C(n, m):
    if m < 0:
        return 0
    return FACTORIALS[n] * rev(FACTORIALS[m]) * rev(FACTORIALS[n-m]) % MOD

class Solution:
    def numberOfStableArrays(self, zero: int, one: int, limit: int) -> int:
        
        @lru_cache(None)
        def dp(n,m,k,p):
            # n -> zero, m -> one, k -> pre count, p -> pre element
            if n + (1-p) * k > limit * (m+1) or m + p * k > limit * (n+1):
                return 0
            elif n + (1-p) * k <= limit and m + p * k <= limit:
                ans = C(n+m, n)
            elif p == 0 and n + k <= limit and m > limit and m <= 2 * limit:
                ans = C(n+m, n) - C(n+m-limit-1, n) * (n+1)
            elif p == 1 and n > limit and m + k <= limit and n <= 2 * limit:
                ans = C(n+m, n) - C(n+m-limit-1, m) * (m+1)
            else:
                ans = 0
                if k*(1-p)+1 <= limit:
                    ans = (ans + dp(n-1, m, k*(1-p)+1, 0)) % MOD
                if k*p+1 <= limit:
                    ans = (ans + dp(m-1, n, k*p+1, 0)) % MOD
            return ans % MOD
        
        ans = dp(zero, one, 0, 0)
        return ans

这个实现基本就是翻译了一下我们的上述实现,在题目3129上的评测结果如下:耗时3727ms,占用内存756.5MB。

2. 第二版本

然后,我们注意到这里的n, m事实上是完全等价的,因此,我们就可以取消掉p这个元素,也就是无需再记录前一个元素是什么,直接对换n,m的值即可,选用是默认从n当中进行元素选择作为开头,这样就可以进一步提升cache的利用率了。

给出第二版代码实现如下:

MOD = 10**9+7
FACTORIALS = [1 for _ in range(401)]
for i in range(1, 401):
    FACTORIALS[i] = i * FACTORIALS[i-1] % MOD
    
def rev(x):
    return pow(x, -1, MOD)

def C(n, m):
    if m < 0:
        return 0
    return FACTORIALS[n] * rev(FACTORIALS[m]) * rev(FACTORIALS[n-m]) % MOD

class Solution:
    def numberOfStableArrays(self, zero: int, one: int, limit: int) -> int:
        
        @lru_cache(None)
        def dp(n,m,k):
            if n + k > limit * (m+1) or m > limit * (n+1):
                return 0
            elif n + k <= limit and m <= limit:
                ans = C(n+m, n)
            elif n + k <= limit and m > limit and m <= 2 * limit:
                ans = C(n+m, n) - C(n+m-limit-1, n) * (n+1)
            else:
                ans = dp(m-1, n, 1)
                if k+1 <= limit:
                    ans = (ans + dp(n-1, m, k+1)) % MOD
            return ans % MOD
        
        ans = dp(zero, one, 0)
        return ans

提交代码评测得到:耗时3157ms,占用内存684.4MB。

3. 第三版本

然后,我们注意到这个k也很碍事,既然n,m的地位完全等价了,我们只需要默认每次都必须从n当中选择1到limit个元素即可,这样就可以去掉k这个参数了,可以进一步优化cache。

MOD = 10**9+7
FACTORIALS = [1 for _ in range(401)]
for i in range(1, 401):
    FACTORIALS[i] = i * FACTORIALS[i-1] % MOD
    
def rev(x):
    return pow(x, -1, MOD)

def C(n, m):
    if m < 0:
        return 0
    return FACTORIALS[n] * rev(FACTORIALS[m]) * rev(FACTORIALS[n-m]) % MOD

class Solution:
    def numberOfStableArrays(self, zero: int, one: int, limit: int) -> int:
        
        @lru_cache(None)
        def dp(n,m):
            if n == 0:
                return 0
            elif m == 0:
                return 1 if n <= limit else 0
            elif n > limit * (m+1) or m > limit * n:
                return 0
            elif n <= limit and m <= limit:
                ans = C(n+m-1, m)
            elif n <= limit and m > limit and m <= 2 * limit:
                ans = C(n+m-1, m) - C(n+m-limit-2, n-1) * n
            else:
                ans = 0
                for i in range(1, min(limit, n) + 1):
                    ans = (ans + dp(m, n-i))
            return ans % MOD
        
        ans = (dp(zero, one) + dp(one, zero)) % MOD
        return ans

提交代码评测得到:耗时707ms,占用内存32.1MB。

4. 第四版本

最后,我们还对上述 C n m C_{n}^{m} Cnm的实现进行了一下优化,具体来说的话就是每次都算pow(n, -1 MOD)太耗时了,因此我们也像阶乘一样预先算好保存在一个数组当中即可。

给出python代码实现如下:

MOD = 10**9+7
FACTORIALS = [1 for _ in range(401)]
for i in range(1, 401):
    FACTORIALS[i] = i * FACTORIALS[i-1] % MOD
    
Inv_FACTORIALS = [pow(x, -1, MOD) for x in FACTORIALS]

def C(n: int, m: int):
    if m < 0:
        return 0
    return (FACTORIALS[n] * Inv_FACTORIALS[m] * Inv_FACTORIALS[n-m]) % MOD

class Solution:
    def numberOfStableArrays(self, zero: int, one: int, limit: int) -> int:
        
        @lru_cache(None)
        def dp(n,m):
            if n == 0:
                return 0
            elif m == 0:
                return 1 if n <= limit else 0
            elif n > limit * (m+1) or m > limit * n:
                return 0
            elif n <= limit and m <= limit:
                ans = C(n+m-1, m)
            elif n <= limit and m > limit and m <= 2 * limit:
                ans = C(n+m-1, m) - C(n+m-limit-2, n-1) * n
            else:
                ans = 0
                for i in range(1, min(limit, n) + 1):
                    ans = (ans + dp(m, n-i))
            return ans % MOD
        
        ans = (dp(zero, one) + dp(one, zero)) % MOD
        return ans

提交代码评测得到:耗时574ms,占用内存32MB。

3. 算法优化

不过可惜的是,即便如此,上述的算法依然无法通过题目3130的测试样例,还是会出现超时的情况。因此在下面的小节里面,我们摘录了两个大佬的两个算法实现,分别来自题目3129和题目3130的解答当中耗时最优的方法,然后稍微用我自己感觉更好理解的方式稍微翻译了一下,虽然我自己依然没有完全看明白具体的数学含义就是了。

不过插句题外话,虽然代码实现两个算法不太一样,不过从具体的思路以及参数计算来看,我觉得这俩实现很可能来自同一个大佬……

只能说,大佬牛逼……

1. 算法实现一

MOD = 10**9+7
FACTORIALS = [1 for _ in range(1001)]
for i in range(1, 1001):
    FACTORIALS[i] = i * FACTORIALS[i-1] % MOD
    
Inv_FACTORIALS = [pow(x, -1, MOD) for x in FACTORIALS]

def C(n: int, m: int):
    if m < 0:
        return 0
    return (FACTORIALS[n] * Inv_FACTORIALS[m] * Inv_FACTORIALS[n-m]) % MOD

class Solution:
    def numberOfStableArrays(self, zero: int, one: int, limit: int) -> int:
        ans = 0
        N = zero + one
        min_zero_group = (zero - 1) // limit + 1
        min_one_group = (one - 1) // limit + 1
        
        @lru_cache(None)
        def count(n, g, k):
            ans = C(n+g-1, g-1)
            r, flag = 1, -1
            while n - r * (k+1) >= 0:
                ans = (ans + flag * C(n - r*(k+1) + g-1, g-1) * C(g, r)) % MOD
                r += 1
                flag *= -1
            return ans
        
        for n in range(min_zero_group, zero+1):
            for m in range(n-1, n+1+1):
                if m < min_one_group or m > one:
                    continue
                flag = 1 if n != m else 2
                ans = (ans + flag * count(zero-n, n, limit - 1) * count(one-m, m, limit - 1)) % MOD
        return ans % MOD

提交代码评测得到:耗时72ms,占用内存17.1MB。

2. 算法实现二

MOD = 10**9+7
FACTORIALS = [1 for _ in range(1001)]
for i in range(1, 1001):
    FACTORIALS[i] = i * FACTORIALS[i-1] % MOD
    
Inv_FACTORIALS = [pow(x, -1, MOD) for x in FACTORIALS]

def C(n: int, m: int):
    if m < 0:
        return 0
    return (FACTORIALS[n] * Inv_FACTORIALS[m] * Inv_FACTORIALS[n-m]) % MOD

class Solution:
    
    def numberOfStableArrays(self, zero: int, one: int, limit: int) -> int:
        ans = 0
        N = zero + one
        min_zero_group = (zero - 1) // limit + 1
        min_one_group = (one - 1) // limit + 1
        
        def count(n, g, k):
            ans = C(n+g-1, g-1)
            r, flag = 1, -1
            while n - r * (k+1) >= 0:
                ans = (ans + flag * C(n - r*(k+1) + g-1, g-1) * C(g, r)) % MOD
                r += 1
                flag *= -1
            return ans
        
        for n in range(min_zero_group, zero+1):
            for m in range(n-1, n+1+1):
                if m < min_one_group or m > one:
                    continue
                flag = 1 if n != m else 2
                ans = (ans + flag * count(zero-n, n, limit - 1) * count(one-m, m, limit - 1)) % MOD
        return ans % MOD

提交代码评测得到:耗时94ms,占用内存16.7MB。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/594169.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

.排序总讲.

在这里赘叙一下我对y总前四节所讲排序的分治思想以及递归的深度理解。 就以788.逆序对 这一题来讲&#xff08;我认为这一题对于分治和递归的思想体现的淋淋尽致&#xff09;。 题目&#xff1a; 给定一个长度为 n&#x1d45b; 的整数数列&#xff0c;请你计算数列中的逆序对…

易语言IDE界面美化支持库

易语言IDE界面美化支持库 下载下来可以看到&#xff0c;是一个压缩包。 那么&#xff0c;怎么安装到易语言中呢&#xff1f; 解压之后&#xff0c;得到这两个文件。 直接将clr和lib丢到易语言安装目录中&#xff0c;这样子就安装完成了。 打开易语言&#xff0c;在支持库配置…

C#-快速剖析文件和流,并使用(持续更新)

目录 一、概述 二、文件系统 1、检查驱动器信息 2、Path 3、文件和文件夹 三、流 1、FileStream 2、StreamWriter与StreamReader 3、BinaryWriter与BinaryReader 一、概述 文件&#xff0c;具有永久存储及特定顺序的字节组成的一个有序、具有名称的集合&#xff1b; …

【系统架构师】-选择题(十三)

1、在某企业的营销管理系统设计阶段&#xff0c;属性"员工"在考勤管理子系统中被称为"员工"&#xff0c;而在档案管理子系统中被称为"职工"&#xff0c;这类冲突称为&#xff08; 命名冲突&#xff09;。 同一个实体在同系统中存在不同的命名&am…

【4087】基于小程序实现的电影票订票小程序软件

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;ssm 【…

局部性原理和磁盘预读

局部性原理 磁盘预读 \

Linux 基础命令、性能监控

一、Linux 基础命令 grep&#xff1a;在文件中执行关键词搜索&#xff0c;并显示匹配的结果。 -c 仅显示找到的行数 -i 忽略大小写 -n 显示行号 -v 反向选择: 仅列出没有关键词的行 (invert) -r 递归搜索文件目录 -C n 打印匹配行的前后 n 行grep login user.cpp # 在…

编译官方原版的openwrt并加入第三方软件包

最近又重新编译了最新的官方原版openwrt-2305&#xff08;2024.3.22&#xff09;&#xff0c;此处记录一下以待日后参考。 目录 1.源码下载 1.1 通过官网直接下载 1.2 映射github加速下载 1.2.1 使用github账号fork源码 1.2.2 创建gitee账号映射github openwrt 2.编译准…

cordova build android 下载gradle太慢

一、 在使用cordova run android / cordova build android 的时候 gradle在线下载 对于国内的链接地址下载太慢。 等待了很长时间之后还会报错。 默认第一次编译在线下载 gradle-7.6.1-all.zip 然后解压缩到 C:\Users\Administrator\.gradle 文件夹中,下载慢导致失败。 二…

(论文阅读-优化器)A Cost Model for SPARK SQL

目录 Abstract 1 Introduction 2 Related Work 3 Background and Spark Basics 4 Cost Model Basic Bricks 4.1 Cluster Abastraction and Cost Model Parameters 4.2 Read 4.3 Write 4.4 Shuffle Read 4.5 Broadcast 5 Modeling GPSJ Queries 5.1 Statistics and S…

【论文阅读笔记】Order Matters(AAAI 20)

个人博客地址 注&#xff1a;部分内容参考自GPT生成的内容 论文笔记&#xff1a;Order Matters&#xff08;AAAI 20&#xff09; 用于二进制代码相似性检测的语义感知神经网络 论文:《Order Matters: Semantic-Aware Neural Networks for Binary Code Similarity Detection》…

Spring Boot 整合 socket 实现简单聊天

来看一下实现的界面效果 pom.xml的maven依赖 <!-- 引入 socket --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><!-- 引入 Fastjson &#x…

【CV-CUDA实战】使用Python+TensorRT+CVCUDA优化YOLOv8

目录 什么是CV-CUDA环境准备准备CV-CUDA静态库解压添加至变量将PyBind静态库复制到env下算子设计前处理算子 TensorRT模型加载后处理函数 完整代码输出演示为什么重新写了&#xff1f;结语 什么是CV-CUDA NVIDIA CV-CUDA™ 是一个开源项目&#xff0c;用于构建云规模人工智能 (…

【数据结构(邓俊辉)学习笔记】列表02——无序列表

文章目录 0.概述1.插入与构造1.1 插入1.1.1 前插入1.1.2后插入1.1.3 复杂度 1.2 基于复制构造1.2.1 copyNodes()1.2.2 基于复制构造1.2.3 复杂度 2.删除与析构2.1 删除2.1.1 实现2.1.2 复杂度 2.2 析构2.2.1 释放资源及清除节点2.2.2 复杂度 3.查找3.1 实现3.2 复杂度 4.唯一化…

每天五分钟深度学习:数学中常见函数中的导数

本文重点 导数是微积分学中的一个核心概念,它描述了函数在某一点附近的变化率。在物理学、工程学、经济学等众多领域中,导数都发挥着极其重要的作用。本文旨在详细介绍数学中常见函数的导数,以期为读者提供一个全面而深入的理解。 数学中常见的导数 常数函数的导数 对于常数…

Raft共识算法笔记,MIT6.824,

处理leader和follow的一个重要思路是多数投票&#xff0c;确保系统中存在奇数个服务器&#xff08;例如3台&#xff09;。进行任何操作都需要来自多数服务器的同意&#xff0c;例如3台服务器中的2台。如果没有多数同意&#xff0c;系统会等待。为什么多数投票有助于避免脑裂问题…

springboot项目 字典/枚举翻译 终极解决方案 AOP+自定义注解+递归实体字段+实体动态三级缓存+责任链+多种转换方式

目录 前言实现思路技术确定 食用方式效果使用样例项目中使用第一步 复制包第二步 实现LoadDictDatabase并将其注入容器第三步 标识需要翻译的字段第四步 标识需要翻译的方法第五步 调用需要翻译的方法 实现细节TODO 前言 字典,即在存储介质中进行存储时,为了避免业务上对其名称…

计数排序,基数排序,桶排序

目录 计数排序: 基数排序&#xff1a; 桶排序: 计数排序: 计数排序是一种非比较型整数排序算法&#xff0c;特别适用于一定范围内的整数排序。它的核心思想是使用一个额外的数组&#xff08;称为计数数组&#xff09;来计算每个值的出现次数&#xff0c;然后根据这些计数信…

[贪心] 区间选点问题

905. 区间选点 - AcWing题库 思路&#xff1a;就是将所有区间按照右端点排序&#xff0c; 然后选取一些区间的右端点 代码&#xff1a; #include <iostream> #include <algorithm> #include <vector> using namespace std; const int N 100010;typedef p…

Flask与HTTP

一、请求响应循环 “请求-响应循环”&#xff1a;客户端发出请求&#xff0c;服务器处理请求并返回响应。 Flask Web程序的工作流程&#xff1a; 当用户访问一个URL&#xff0c;浏览器便生成对应的HTTP请求&#xff0c;经由互联网发送到对应的Web服务器。Web服务器接收请求&a…
最新文章