当前位置: 首页 > news >正文

Leetcode 698. 划分为k个相等的子集

1.题目描述

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


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


输入: nums = [1,2,3,4], k = 3
输出: false


提示:

  • 1 <= k <= len(nums) <= 16
  • 0 < nums[i] < 10000
  • 每个元素的频率在 [1,4] 范围内

2.思路分析

特殊情况判断:

  • 如果数组 nums 的和 sum 不能整除 k,或者数组 nums 中的最大值大于每个子集的和 sum / k (也就是 target),返回 false。
totalSum = sum(nums)
# 预处理 特殊情况处理
if totalSum % k != 0:
    return False
target = totalSum // k
bucket = [0] * (k + 1)
# 排序(降序) 方便后面剪枝
nums.sort(reverse=True)
# 如果最小的元素大于target 直接返回False
if nums[-1] > target:
    return False

以数字为视角: 每个数字每一次的可选择项都为「所有桶」,所以每一次可选择项均为桶的数量 k。故时间复杂度指数的底数为 k

回溯三部曲:

确定回溯函数要处理的参数以及返回值

  • **参数:**num: 输入数组 k: 桶个数 startIndex: 当前元素对应索引 target: 目标值
  • 返回值:boolean
def backtrack(nums: List[int], k: int, bucket: List[int], startIndex: int, target: int) -> bool:

确定终止条件

nums 中的数字全部消耗完,说明找到了 k 个桶,且每个桶内的数字和为 target。

if startIndex == len(nums):
    # 判断现在桶中的元素是否符合要求 -> 路径是否满足要求
	for i in range(k):
		if bucket[i] != target:
			return False
	return True

单层搜索逻辑

  • 依次遍历每个桶,尝试将当前的数字 nums[startIndex] 放入到该桶中,看最终是否能够凑成 k 个桶。
  • 如果当前遍历到的桶加上 nums[startIndex] > 目标值 target,则说明 nums[startIndex] 不应该放到当前桶内,继续遍历下一个桶。
  • 当前桶的数字和与上一个桶一样,跳过。
# 单层搜索逻辑
for i in range(k):
    # 去重优化
    if i > 0 and bucket[i] == bucket[i - 1]:
        continue
    # 剪枝: 如果当前元素的值+bucket[i]>target,选择下一个桶
    if bucket[i] + nums[startIndex] > target:
        continue
    # 处理元素
    bucket[i] += nums[startIndex]
    # 处理下一个元素(递归)
    if backtrack(nums, k, bucket, startIndex + 1, target):
        return True
    # 撤销选择
    bucket[i] -= nums[startIndex]
# 遍历结束,k个桶都不满足要求
return False

3.代码实现

class Solution:
    def canPartitionKSubsets(self, nums: List[int], k: int) -> bool:
        totalSum = sum(nums)
        # 预处理 特殊情况处理
        if totalSum % k != 0:
            return False
        target = totalSum // k
        bucket = [0] * (k + 1)
        # 排序优化
        nums.sort(reverse=True)
        if nums[-1] > target:
            return False

        # 回溯函数要处理的参数以及返回值
        def backtrack(nums: List[int], k: int, bucket: List[int], startIndex: int, target: int) -> bool:
            """
            Args:
            :param nums: 输入数组
            :param k: 桶个数
            :param startIndex: 当前元素对应索引
            :param target: 目标值
            :return:
            """
            # 回溯终止条件:处理完所有元素以及当前桶中的元素是否符合要求
            # 结束条件优化
            if startIndex == len(nums):
                # 判断现在桶中的元素是否符合要求 -> 路径是否满足要求
                for i in range(k):
                    if bucket[i] != target:
                        return False
                return True
            # 单层搜索逻辑
            for i in range(k):
                # 去重优化
                if i > 0 and bucket[i] == bucket[i - 1]:
                    continue
                # 剪枝: 如果当前元素的值+bucket[i]>target,选择下一个桶
                if bucket[i] + nums[startIndex] > target:
                    continue
                # 处理元素
                bucket[i] += nums[startIndex]
                # 处理下一个元素(递归)
                if backtrack(nums, k, bucket, startIndex + 1, target):
                    return True
                # 撤销选择
                bucket[i] -= nums[startIndex]
            # 遍历结束,k个桶都不满足要求
            return False
        return backtrack(nums, k, bucket, 0, target)

复杂度分析: todo

相关文章:

  • 开发工具安装
  • 图解字符串匹配算法:从Brute-Force到KMP,一下子就整明白了
  • Python语言:散修笔记
  • 为什么要学习Linux内核,如何学习?
  • 块级作用域绑定
  • 8.7 迁移学习域适应
  • 高企认定评分标准有哪些?
  • halcon提取数据集中指定图片并进行裁剪
  • 使用PdfSharp从模板生成Pdf文件
  • HTML篇三——(2)
  • 【012】基于JavaWeb酒店客房管理系统(附源码、数据库、数据库文档、运行教程)
  • Gitee账号注册以及Git下载安装
  • 边学边记——Java中有关接口的知识
  • ant-design-vue 库 Loading 组件封装
  • 2022 年前端趋势的 技术发展情况
  • 【React系列】如何构建React应用程序
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • ABAP的include关键字,Java的import, C的include和C4C ABSL 的import比较
  • Android 架构优化~MVP 架构改造
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • CentOS从零开始部署Nodejs项目
  • css的样式优先级
  • ES6系统学习----从Apollo Client看解构赋值
  • Facebook AccountKit 接入的坑点
  • orm2 中文文档 3.1 模型属性
  • REST架构的思考
  • 对超线程几个不同角度的解释
  • 基于Android乐音识别(2)
  • 排序算法学习笔记
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 走向全栈之MongoDB的使用
  • CMake 入门1/5:基于阿里云 ECS搭建体验环境
  • hi-nginx-1.3.4编译安装
  • MyCAT水平分库
  • 哈罗单车融资几十亿元,蚂蚁金服与春华资本加持 ...
  • ​ssh-keyscan命令--Linux命令应用大词典729个命令解读
  • # C++之functional库用法整理
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • (C++)八皇后问题
  • (C语言)fread与fwrite详解
  • (Forward) Music Player: From UI Proposal to Code
  • (Python) SOAP Web Service (HTTP POST)
  • (Redis使用系列) Springboot 在redis中使用BloomFilter布隆过滤器机制 六
  • (ZT)出版业改革:该死的死,该生的生
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (附源码)ssm基于jsp高校选课系统 毕业设计 291627
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (解决办法)ASP.NET导出Excel,打开时提示“您尝试打开文件'XXX.xls'的格式与文件扩展名指定文件不一致
  • (九)信息融合方式简介
  • (力扣)1314.矩阵区域和
  • .libPaths()设置包加载目录
  • .Net - 类的介绍
  • .Net core 6.0 升8.0
  • .NET Framework杂记