給定一個整數數組 nums 和一個正整數 k,找出是否有可能把這個數組分成 k 個非空子集,其總和都相等。
示例 1:
輸入: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
輸出: True
說明: 有可能將其分成 4 個子集(5),(1,4),(2,3),(2,3)等於總和。
注意:
1 <= k <= len(nums) <= 16
0 < nums[i] < 10000
解題思路:
1.典型的遞歸可以解決的問題,對於某個子集,其差集執行類似邏輯即可
2.求子集是一個慣用方法
3.要想基本ac不超時,加入剪枝方法 sum(nums) / k,我表示此trick沒有想到,看官方題解想到的,還是腦子不活啊
4.自己面對部分用例超時的時候,進行了提前求和判斷的剪枝技巧,但是沒有啥效果,或者說剪枝力度不夠,遠沒有上面的trick有效果
5.具體詳見下面代碼,時間複雜度不好具體分析,因爲有各種剪枝限制,但基本的粗估複雜度是O(k * 2^n),一共k步,每一步都涉及到了求子集的情況;空間複雜度也是類似道理,每一步都有數組來存儲所有滿足條件的子集和對應差集,O(k * 2^n * n)
成績:
執行用時 :772 ms, 在所有 Python 提交中擊敗了11.77%的用戶
內存消耗 :19.4 MB, 在所有 Python 提交中擊敗了33.33%的用戶
AC代碼(python):
class Solution(object):
def canPartitionKSubsets(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: bool
基本思路:
1.找所有和爲定值的且長度不大於固定個數的子集
2.遞歸調用
"""
# 拷貝數組
def copy_list(mlist):
newlist = []
for value in mlist:
newlist.append(value)
return newlist
# 獲取數組差集
def get_remain(nums, one):
one = copy_list(one)
remain = []
for num in nums:
is_find = False
for idx, m in enumerate(one):
if num == m:
del one[idx]
is_find = True
break
if not is_find:
remain.append(num)
return remain
# 查找滿足指定和且長度不超過max_len的子集和對應差集數組
def find_subs(nums, need_sum, max_len):
result = []
subs = []
for idx, num in enumerate(nums):
if idx == 0:
newlist = [num]
if len(newlist) <= max_len:
if sum(newlist) == need_sum:
result.append(newlist)
if len(newlist) < max_len:
subs.append(newlist)
else:
mlen = len(subs)
for idx in range(mlen):
newlist = copy_list(subs[idx])
newlist.append(num)
if len(newlist) <= max_len:
if sum(newlist) == need_sum:
result.append(newlist)
if len(newlist) < max_len:
subs.append(newlist)
real_result = []
for one in result:
real_result.append((one, get_remain(nums, one)))
return real_result
# !!! 題目trick 重要的剪枝方法 sum(nums) / k 沒有這個邏輯,部分用例超時
need_sum = sum(nums) / k
# 遞歸查找的方法,返回是否可分割爲k個且和均爲need_num的子集
def mfind(nums, k):
if len(nums) < k:
return False
if k == 0:
if len(nums) == 0:
return True
return False
if sum(nums) % k != 0:
return False
subs = find_subs(nums, need_sum, len(nums)-k+1)
for sub, remain in subs:
if mfind(remain, k-1):
return True
return False
return mfind(nums, k)