leetcode 698. 划分为k个相等的子集-状态压缩+记忆搜索的一步步实现

news2024/9/27 19:26:47

题目

给定一个整数数组 nums 和一个正整数 k,找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。

示例

输入: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
输出: True
说明: 有可能将其分成 4 个子集(5),(1,4),(2,3),(2,3)等于总和。

题解

该题目的意思是给你一堆数据nums,你要把这些数据划分成k个集合,每个集合里的数据和是相同的。以nums=[1,3, 3, 2, 3, 5, 2, 1],k=4为例来说明一下该题目的思路。
在这里插入图片描述
首先来分析题目,题目的意思就是把给定的可选数据池分到k=4个桶里,每个桶中的数据和相同,那很显示有一个特性就是k*每个桶中数据和=可选数据池所有数据的和,在这个示例里每个桶中包含的数据和为5。通过这一步分析我们能得到一个新的条件即每个桶中数据和,同时也能确定两个边界:1 必须满足sum(nums)%k=0,即所有数据和必须能被k整除;2 len(num)>k,即可选数据池的数据个数必须大于k不然的话k个桶中就会出现空集。

下面探索一下该题目的可行算法。初始状态可选数据集为当前数据列表,所有的桶中均为空。
在这里插入图片描述
由于目标是为了让每个桶中的数据达到5。我们可以遍历可选择数据池,选择数据放到桶中。由于当前桶中均为空是一样的,可以随意选择一个桶放入数据1,结果如下图所示。
在这里插入图片描述
接下来处理数字3,在这里当前的桶中有一个里面有数据了,这里我们首先需要尽可以找到一组数来填满一个桶,然后再填下一个桶。把3放到第一个桶中,这样第一个桶中的数据和就变成了4。
在这里插入图片描述
继续下去,碰到当前数据池中第2个3,由于该数据如果放到第一个桶中的话会超出目标,因此,跳过3,直到1才能满足要求。最终结果如下图。
在这里插入图片描述
针对该问题大体思路已出现,但是还有一个关键的问题即第一个桶中选完1之后,3一定是放在第一个桶中的么,其实不一定。这里是本题的关键,其中涉及到递归或回溯思想来解决该问题。

其实我们在之前的步骤中也看到了,我们进行划分的时候只跟两个变量有关,一是当前值,一是可选数据池,我们每一步的目标均是希望实现剩下的所有桶均达到目标值。如果一个桶满了当前值就需要再从0开始算,如下图所示。

在这里插入图片描述
我们的递归函数的已经很明显了:

  • 输入:可选数据池,当前值
  • 输出:是否能填充剩下的桶
  • 递归过程:终止条件是可选数据池数据为空,如果已选数据与当前值大于目标则跳过,否则更新当前值与可选择数据池执行递归过程。

到些基础版本的递归代码就可以手写了。

class Solution:
    def canPartitionKSubsets(self, nums: List[int], k: int) -> bool:
        all_sum=sum(nums)
        sub_sum,rest=divmod(all_sum,k)
        #边界,判断是不是
        if rest:
            return False
        if len(nums)<k:
            return False
        def dfs(choosable_list,curr_value):
            #如果可选数据池为空则分配完成
            if not choosable_list:
                return True
            for idx in range(len(choosable_list)):
                #如果当前数与靠近的数据之处大于sub_sum则跳过
                if choosable_list[idx]+curr_value>sub_sum:
                    break
                #复制choosable_list
                choosable_list_copy=[item for item in choosable_list]
                del choosable_list_copy[idx]
                if dfs(choosable_list_copy,(curr_value+choosable_list[idx])%sub_sum):
                    return True
            return False
        return dfs(nums,0)

代码会超时。这里需要进步一优化。

状态压缩

首先考虑的是状态压缩,因为chooselist使用的话需要复杂副本,占用较大空间。这里可选数据池满足状态压缩的条件,要求的长度小于16,使用32位二进制可以有效表示可选数据的状态。这里二进制中的1表示nums列表中该位置的数据可选,0表示该位置不可选择。初始状态是(1<<n)-1,n是nums的长度,表示0到n-1位全为1。这里与上面使用列表不同,需要判断当前状态是否可选择,state>>i&1即判断nums[i]的数据是否可选。在递归时,需要更新可选择列表的状态,state^ (1<<i)是利用位运算把第i位的状态变成0,这里^ 有个性质,0与任何数做^ 运算均不变,而当前i位置是1再与一个i位置为1做^运算即变为0。这样基于状态压缩的代码即如下实现。

class Solution:
    def canPartitionKSubsets(self, nums: List[int], k: int) -> bool:
        n=len(nums)
        all_sum=sum(nums)
        sub_sum,rest=divmod(all_sum,k)
        #边界
        if rest:
            return False
        #如果nums的长度小于k
        if n<k:
            return False
        def dfs(state,curr_value):
            #如果没有可选择的数据则分配完成
            if state==0:
                return True
            for i in range(n):
                #判断i位置数据是否可选
                if state>>i&1:
                    #判断该数据是否可以与curr_value分到一起
                    if nums[i]+curr_value>sub_sum:
                        break
                    if dfs(state^(1<<i),(curr_value+nums[i])%sub_sum):
                        return True
            return False
        return dfs((1<<n)-1,0)

依然超时。

记忆化搜索

上面代码超时的原因是递归过程会有大量的重复计算,这里显示考虑记忆化搜索,python中记忆化搜索实现非常简单,在递归函数前加上@cache装饰器。

class Solution:
    def canPartitionKSubsets(self, nums: List[int], k: int) -> bool:
        n=len(nums)
        all_sum=sum(nums)
        sub_sum,rest=divmod(all_sum,k)
        #边界
        if rest:
            return False
        #如果nums的长度小于k
        if n<k:
            return False
        @cache
        def dfs(state,curr_value):
            #如果没有可选择的数据则分配完成
            if state==0:
                return True
            for i in range(n):
                #判断i位置数据是否可选
                if state>>i&1:
                    #判断该数据是否可以与curr_value分到一起
                    if nums[i]+curr_value>sub_sum:
                        break
                    if dfs(state^(1<<i),(curr_value+nums[i])%sub_sum):
                        return True
            return False
        return dfs((1<<n)-1,0)

计算复杂度

  • 时间复杂度: O ( n × 2 n ) O(n×2^n) O(n×2n) ,其中 n 为数组 nums 的长度,共有 2 n 2^n 2n个状态,每一个状态进行了 n 次尝试。
  • 空间复杂度: O ( 2 n ) O(2^n) O(2n),其中n 为数组 nums 的长度,主要为状态数组的空间开销。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

利用OpenCV的函数LUT()对矩阵的数据进行查表映射

利用OpenCV的函数LUT()对矩阵的数据进行查表映射 LUT是Look Up Table 的缩写&#xff0c;意为查表映射。 OpenCV的函数LUT()能实现图像灰度值或者说矩阵元素值的查表映射功能。 函数LUT()的C原型如下&#xff1a; void cv::LUT(InputArray src,InputArray lut,OutputArray …

XStream常用注解学习

XStream中文教程&#xff1a;https://www.wenjiangs.com/doc/iyx6stww 参考博客&#xff1a;https://www.jb51.net/article/201309.htm 用在xml中&#xff0c;常用注解&#xff1a; XStreamAliasType(value“要修改成的全限定名”): 包名修改 XStreamAlias(“user”) : 修改类,…

力扣hot100——第5天:22括号生成、23合并K个升序链表、31下一个排列

文章目录1.22括号生成1.1.题目1.2.题解2.23合并K个升序链表2.1.题目2.2.解答3.31下一个排列3.1.题目3.2.解答1.22括号生成 参考&#xff1a;力扣题目链接&#xff1b;题解1&#xff0c;题解2 1.1.题目 1.2.题解 这道题目是使用递归的方法来求解&#xff0c;因为要求解所有的…

这个macOS神器,让爱怀旧的人直呼:“爷青回!”

写在前面 Hello&#xff0c;大家好&#xff0c;我们又见面了。 停止更新了两周多&#xff0c;本来打算荒废这个CSDN的&#xff0c;但对写文章的热爱又逼着我继续写…… 这次我们要推荐一个macOS神器&#xff0c;叫“Aqua Menu Bar”。 以后永远不写水文了&#xff0c;告别CS…

AJAX异步请求解决跨域问题的三种方式

一 什么是跨域 出于浏览器的同源策略限制。同源策略&#xff08;Sameoriginpolicy&#xff09;是一种约定&#xff0c;它是浏览器最核心也最基本的安全功能&#xff0c;如果缺少了同源策略&#xff0c;则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的…

移动应用测试场景的五个重点

移动应用程序测试在移动开发生命周期中至关重要。开发人员和应用程序测试人员在上线之前应该考虑不同的移动应用程序测试场景。考虑到每天发布的应用程序数量&#xff0c;这一点尤为重要。根据 Statista 的数据&#xff0c;2020 年全球移动应用下载量已增至 2180 亿次。这导致了…

通过.sh文件快捷部署jar包到服务器上

参考博客&#xff1a;https://blog.csdn.net/qq_43382350/article/details/125008727 直接写一个脚本文件&#xff0c;每次运行这个文件就可以通过.sh文件快捷部署jar包到服务器上。 在合适的文件夹下创建脚本文件 vim start.sh 内容如下(三个jar包分别放置在a1、a2、a3文件夹…

SAR雷达系统反设计及典型目标建模与仿真实现研究——目标生成与检测(Matlab代码实现)

&#x1f352;&#x1f352;&#x1f352;欢迎关注&#x1f308;&#x1f308;&#x1f308; &#x1f4dd;个人主页&#xff1a;我爱Matlab &#x1f44d;点赞➕评论➕收藏 养成习惯&#xff08;一键三连&#xff09;&#x1f33b;&#x1f33b;&#x1f33b; &#x1f34c;希…

白话强化学习(理论+代码)

文章目录前言强化学习概述案例alphaGo无人驾驶why强化学习特点基本理论部分基本概念马尔可夫模型马尔可夫链案例马尔科夫决策过程累计回报概念及其求取流程案例算法目的Q-Leaning真实值与预测值案例离线学习Sarsa选择动作函数代码DQN流程预估“表”与实际“表”编码坑点环境修改…

你的业务代码中Spring声明式事务处理正确了吗?

Spring 针对 Java Transaction API (JTA)、JDBC、Hibernate 和 Java Persistence API(JPA) 等事务 API&#xff0c;实现了一致的编程模型&#xff0c;而 Spring 的声明式事务功能更是提供了极其方便的事务配置方式&#xff0c;配合 Spring Boot 的自动配置&#xff0c;大多数 S…

试剂的制备丨艾美捷逆转录病毒定量试剂盒方案

QuickTiter逆转录病毒定量试剂盒提供了一种测定逆转录病毒滴度的快速方法。该测定法测量逆转录病毒的病毒核酸含量&#xff0c;可以在纯化病毒之前或之后进行。 Cell Biolabs艾美捷QuickTiter™ 逆转录病毒定量试剂盒不涉及细胞感染&#xff1b;相反&#xff0c;它专门测量纯化…

Linux | 可重入函数 | volatile | SIGCHLD信号

文章目录可重入函数volatilevolatile和const同时修饰变量SIGCHLD信号可重入函数 当一个函数可以被两个流调用&#xff0c;我们称该函数具有重入特征 如果一个函数被重入后可能导致内存泄漏的问题&#xff0c;我们称该函数为不可重入函数&#xff0c;反之&#xff0c;一个函数…

BER转Q

BER转Q Q(2^0.5)*erfcinv(2*BER) Q_dB20*log10(Q) 1、为什么要这样转&#xff1a; 暂时我也不知道&#xff0c;知道了再来补 2、关于erfcinv&#xff1a; yerf(x) 误差函数 yerfc(x) 互补误差函数 yerfinv(x) 逆误差函数(误差函数的反函数) yerfcinv(x) 逆互补误差函数(互补误差…

测试网络、磁盘使用情况和最大性能

1、测最大网络带宽&#xff0c;当前流量 查看网卡信息&#xff1a;ethtool p2p1 最简单的方法是用scp复制一个大文件&#xff0c;例如50G&#xff0c;复制时间要长&#xff0c;至少30分钟。之前在数据库迁移时&#xff0c;发现网速对迁移速度导致了重大影响&#xff0c;我们的…

基于go-micro微服务的实战-Gateway网关层的限流降级(八)

基于go-micro微服务的实战-Gateway网关层的限流降级(八) 文章最后附带完整代码 这一节主要是在Gateway网关层&#xff0c;基于go-micro的装饰器引入限流和降级。限流降级用的是开源库hystrix,类似java的hystrix&#xff0c;这里不做具体介绍和使用&#xff0c;可自行查看文档。…

车载ECU嵌入式设备的诊断测试 – DTC

作者 | 李伟 上海控安安全测评中心安全测评部总监 来源 | 鉴源实验室 01 DTC-Diagnostic Trouble Code&#xff08;诊断故障代码&#xff09; 车辆在运行的过程当中&#xff0c;控制器会监控状态&#xff0c;特定故障发生时控制器会记录这些故障。车辆送4S店进行维修保养时&…

Numpy入门[4]——数组类型

Numpy入门[4]——数组类型 参考&#xff1a; https://ailearning.apachecn.org/ 使用Jupyter进行练习 import numpy as np之前已经看过整数数组和布尔数组&#xff0c;除此之外还有浮点数数组和复数数组。 复数数组 a np.array([1 1j , 2 , 3 , 4]) aarray([1.1.j, 2.0.j, …

Java基于PHP+MySQL干洗店管理系统的设计与实现

干洗店管理系统是信息时代的产物,它是干洗店管理的一个好帮手。有了它不再需要繁重的纸质登记,有了它干洗店管理员不在需要繁重的工作,一些收费标准和干洗业务等基本信息可以由管理人员及时的对信息进行查询、更新、修改和删除,方便简易,且时效性高。 干洗店管理系统是一个典型…

java 中使用BigDecimal 解决科学计数法问题

一 BigDecimal的Api 1.1 常用方法介绍 ROUND_CEILING 向正无穷方向舍入 ROUND_DOWN 向零方向舍入 ROUND_FLOOR 向负无穷方向舍入 ROUND_HALF_DOWN 向&#xff08;距离&#xff09;最近的一边舍入&#xff0c;除非两边&#xff08;的距离&#xff09;是相等,如果是…

在python 深度学习Keras中计算神经网络集成模型

神经网络的训练过程是一个挑战性的优化过程&#xff0c;通常无法收敛。最近我们被客户要求撰写关于深度学习的研究报告&#xff0c;包括一些图形和统计输出。 这可能意味着训练结束时的模型可能不是稳定的或表现最佳的权重集&#xff0c;无法用作最终模型。 解决此问题的一种…