草庐IT

LeetCode - 698 划分为k个相等的子集

伏城之外 2023-04-22 原文

目录

题目来源

题目描述

示例

提示

题目解析

算法源码


题目来源

698. 划分为k个相等的子集 - 力扣(LeetCode)

题目描述

给定一个整数数组  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] 范围内

题目解析

本题其实是一道子集问题,可以利用回溯法求解。

首先,我们可以求出nums数组的所有元素之和sum,如果sum % k 不为0的话,说明每一份子集的和不是整数,但是题目说了nums数组中的元素都是整数,因此每一个子集的和也应该是整数,因此如果sum % k 不为0的话,则返回false。

如果sum % k 为0,则我们假设subSum = sum / k,即每一个子集的和为subSum,那么subSum应该大于等于nums数组的最大值,因为nums数组的每一个子集至少有一个元素组成,因此subSum应该大于或等于每一个nums元素,即至少不小于nums数组的最大值。

当面两个条件都符合后,还不能说明nums可以被分为k个和相等的子集,比如nums=[2,2,2,2,3,4,5] ,且k=4。

nums数组的和sum = 20,k=4,因此subSum = 5,而subSum >= max(nums[i]) = 5,因此符合上面两个条件,但是nums数组却无法分为4个等和子集。

因此,我们需要一种算法来判断一个数组是否可以划分k个和相等的子集,此时我们就需要借助回溯算法。

我们可以想象划分子集问题,可以看成向k个桶中放球,每个桶的承重为subSum,而现在nums中存放着重量不一的多个球,如果nums中的球都能放到k个桶中,则可以划分子集成功。

比如nums = [4, 3, 2, 3, 5, 2, 1], k = 4,对应的subSum=5

首先,nums的重量5的球可以独占一个桶,我们可以减少考虑一个桶的处理

 接下来,我们需要将球按照降序排序,然后依次放入桶中

 

 

由于 4 + 3 > 5,因此球3需要放到下一个桶中

 

同理处理下面的球

 

 

 

 

这里我们对nums进行了降序排序,而不是升序排序,因为升序排序可能会产生非常复杂的放置行为,这将不利于我们理解。 

上述逻辑对应的算法代码如下

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {boolean}
 */
var canPartitionKSubsets = function(nums, k) {
  const sum = nums.sort((a,b) => b - a).reduce((p, c) => p + c)

  if(sum % k !== 0) return false

  const subSum = sum / k

  if(subSum < nums[0]) return false

  while(nums[0] === subSum) {
      nums.shift()
      k--
  }

  const buckets = new Array(k).fill(0)

  return partition(nums, 0, buckets, subSum)
};

function partition(nums, index, buckets, target) {
    if(index === nums.length) return true

    const selected = nums[index]

    for(let i = 0; i < buckets.length; i++) {
        if(selected + buckets[i] <= target) {
            buckets[i] += selected
            if(partition(nums, index+1, buckets, target)) return true
            buckets[i] -= selected
        }
    }
    return false
}

可以发现,性能非常差。

有这样一种情况,即我们选择了一个球,但是发现此时每一个桶中的球重量之和都相等,也就是说此时选择的球放到哪一个桶中,最终结果都是相同的,比如球放到桶1中重量超了,则放到其他桶中重量肯定也是超的。

但是根本上面算法逻辑,我们肯定会在选择的球放到桶1超了之后,继续到桶2中尝试,而桶2超了之后,必然放到桶3中尝试,这其实就是一种无意义的试探操作。

因此我们再进入递归流程前,先判断 buckets[i]的重量是否和buckets[i-1]的重量是否相等,若相等的话,则buckets[i-1]试过了,则buckets[i]就不用试了。

最终代码如下

 

算法源码

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {boolean}
 */
var canPartitionKSubsets = function(nums, k) {
  const sum = nums.sort((a,b) => b - a).reduce((p, c) => p + c)

  if(sum % k !== 0) return false

  const subSum = sum / k

  if(subSum < nums[0]) return false

  while(nums[0] === subSum) {
      nums.shift()
      k--
  }

  const buckets = new Array(k).fill(0)

  return partition(nums, 0, buckets, subSum)
};

function partition(nums, index, buckets, target) {
    if(index === nums.length) return true

    const selected = nums[index]

    for(let i = 0; i < buckets.length; i++) {
        if (i > 0 && buckets[i] === buckets[i - 1]) continue // 此步剪枝将大幅提升性能
        if(selected + buckets[i] <= target) {
            buckets[i] += selected
            if(partition(nums, index+1, buckets, target)) return true
            buckets[i] -= selected
        }
    }
    return false
}

有关LeetCode - 698 划分为k个相等的子集的更多相关文章

  1. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  2. ruby - Ruby 的 Hash 在比较键时使用哪种相等性测试? - 2

    我有一个围绕一些对象的包装类,我想将这些对象用作散列中的键。包装对象和解包装对象应映射到相同的键。一个简单的例子是这样的:classAattr_reader:xdefinitialize(inner)@inner=innerenddefx;@inner.x;enddef==(other)@inner.x==other.xendenda=A.new(o)#oisjustanyobjectthatallowso.xb=A.new(o)h={a=>5}ph[a]#5ph[b]#nil,shouldbe5ph[o]#nil,shouldbe5我试过==、===、eq?并散列所有无济于事。

  3. ruby-on-rails - ActiveRecord 对象相等 - 2

    根据ActiveRecord::Base的文档:==(comparison_object)Returnstrueifcomparison_objectisthesameexactobject,orcomparison_objectisofthesametypeandselfhasanIDanditisequaltocomparison_object.id.Notethatnewrecordsaredifferentfromanyotherrecordbydefinition,unlesstheotherrecordisthereceiveritself.Besides,ifyoufet

  4. Python 刷Leetcode题库,顺带学英语单词(31) - 2

    ValidPalindromeGivenastring,determineifitisapalindrome,consideringonlyalphanumericcharactersandignoringcases. [#125]Example:"Aman,aplan,acanal:Panama"isapalindrome."raceacar"isnotapalindrome.Haveyouconsiderthatthestringmightbeempty?Thisisagoodquestiontoaskduringaninterview.Forthepurposeofthisproblem

  5. ruby - 是否有内置的 Ruby 1.8.7 将数组拆分为相同大小的子数组? - 2

    我已经开始了:defsplit_array(array,size)index=0results=[]ifsize>0whileindex如果我在[1,2,3,4,5,6]上运行它,比如split_array([1,2,3,4,5,6],3)它将产生这个数组:[[1,2,3],[4,5,6]]。在Ruby1.8.7中是否已经有可用的东西可以做到这一点? 最佳答案 [1,2,3,4,5,6].each_slice(3).to_a#=>[[1,2,3],[4,5,6]]对于1.8.6:require'enumerator'[1,2,3,4

  6. ruby - 以随机顺序将数组拆分为多个数组 - Ruby - 2

    我试图在每次运行时以随机顺序将一个名称数组拆分为多个数组。我知道如何拆分它们:name_array=["bob","john","rob","nate","nelly","michael"]array=name_array.each_slice(2).to_a=>[["bob","john"],["rob","nate"],["nelly","michael"]]但是,如果我希望它每次都以随机顺序吐出它们怎么办? 最佳答案 在做同样的事情之前,打乱数组。(Array#shuffle)name_array.shuffle.each_s

  7. IDEA使用LeetCode插件 - 2

    前言我们习惯用idea编写、调试代码,在LeetCode上刷题时,如果能够在IDEA编写代码,并且做好代码管理,是一件事半功倍的事情。对于后续复习题目,做笔记也会非常便利。本文目的在于介绍LeetCodeEditor的使用,以及配置工具类,最终目录结构如下:note:放置笔记src:放置代码leetcode.editor.cn:插件LeetCodeEditor自动生成utils:自定义的工具包,可用于自动化输入测试用例,定义题目需要的类(结构体)out:运行测试时自动生成LeetCodeEditorGitHub:https://github.com/shuzijun/leetcode-edit

  8. ruby - 使用 Ruby 在数组中查找大小为 N 的所有子集 - 2

    给定一个数组['a','b','c','d','e','f'],我如何获得包含两个的所有子集的列表、三、四元素?我是Ruby的新手(从C#迁移过来),不确定“Ruby之道”是什么。 最佳答案 查看Array#combination然后是这样的:2.upto(4){|n|array.combination(n)} 关于ruby-使用Ruby在数组中查找大小为N的所有子集,我们在StackOverflow上找到一个类似的问题: https://stackoverf

  9. Ruby:检查所有数组元素是否相等 - 2

    我在使用Ruby代码时遇到了一些“问题”。我想检查数组的所有元素是否相等。例如,假设我有一个只有5的数组:arr=[5,5,5,5,5]我知道我可以做类似的事情arr[0]==arr[1]==arr[2]==arr[3]#==arr[4]==...但这对于巨大的数组来说是不可能的,而且在我看来也不是很像Ruby。我们可以通过做这样的事情来改进它:defall_equal?(arr)foriin0..(arr.size-2)ifarr[i]!=arr[i+1]thenreturnfalseendendtrueend但我也认为这很丑陋。那么是否有任何内置/更好/更短(更像Ruby风格)的方

  10. ruby - Ruby 中的 "=="是否总是值相等? - 2

    抱歉,如果重复(我没找到)这只是为了确认Ruby的运算符==始终执行相等比较。IE。a==b将a的值与b的值进行比较,而不是像Java那样比较它们是否指向内存中的同一个对象(对于后者,在Ruby中,您应该使用a.object_id==b.object_id).因此,在Ruby中将字符串值与==进行比较是安全的(而在Java中这样做并不安全)谢谢编辑:问题在于任何Ruby对象的默认==行为,因为它会误导Java-C-C++程序员假设a==b比较引用本身,而不是引用内容。无论如何,你可以检查这段代码,使用字符串one="hello"two="he"two编辑2。所以,在Ruby中,比较a=

随机推荐