划分k个相等的字符集

leetcode#698
给定一个整数数组 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

这个题目,由于k是输入决定的,所以即使想写k重for循环也写不了,并且时间复杂度是O( k n k^n kn),于是尝试递归解决。

思路:
用一个4元状态变量w表示安排第i个数值时每个组内元素和,初始时w为(0,0,0,0)(k=4)。当某个组内元素和大于sum/4时,表示此方案下的递归行不通,返回false;当nums分配完毕时,代表存在分配方案。思路和用递归写01背包问题很像
代码1:

def search(nums,k,w,target):
	if nums==[]:
		return True
	v=nums.pop()
	for i in range(k):
		if v+w[i]<=target:
			w[i]+=v
			if(search(nums,k,w,target)):
				return True
			w[i]-=v
	nums.append(v)
	return False

nums=[4, 3, 2, 3, 5, 2, 1]
k=4
nums.sort()
if sum(nums)%k!=0 or nums[-1]>sum(nums)/k:
	print(False)
else:
	w=[0]*k
	target=sum(nums)/k
	print(search(nums,k,w,target))

不过这样做跟写k个for循环时间复杂度一样,效率不高。于是看了下leetcode官方解答,在这个递归的基础上进行了改进:

  • 首先上面的解法没有注意到每个分组之间的地位相同,逐个进行了尝试;
  • 而实际上如果状态变量w(m,0,0,0)下的分支如果不能找到分配方案,那么w(0,m,0,0),w(0,0,m,0)和w(0,0,0,m)不用进行递归就知道不存在分配方案,这样,对于不可能的方案的递归减少了3/4;
  • 分析可知,如果将某个值分配给第i组,不能找到解决方案,并且w[i]原来为0,那么不用考虑分配给剩下的几组,直接跳出当前for循环(如果w[i]=0, 0<=i<k-1,那么后面的k-i组不用尝试;如果i=k-1,那么本身就是最后一组)。

因此只需要添加一行break代码
代码2:

def search(nums,k,w,target):
	if nums==[]:
		return True
	v=nums.pop()
	for i in range(k):
		if v+w[i]<=target:
			w[i]+=v
			if(search(nums,k,w,target)):
				return True
			w[i]-=v
			if w[i]==0: break
	nums.append(v)
	return False

nums=[4, 3, 2, 3, 5, 2, 1]
k=4
nums.sort()
if sum(nums)%k!=0 or nums[-1]>sum(nums)/k:
	print(False)
else:
	w=[0]*k
	target=sum(nums)/k
	print(search(nums,k,w,target))

很明显这种方法时间复杂度要低,但是具体多少不好分析,官方给出的时间复杂度O(k^(n-k)*k!)。

对于具体分配方案问题,我的想法是递归的时候返回当前物品的状态(分配给1,2,3,…,或者第k组),但这样只能返回一种分配方案,如何返回所有的分配方案目前没有想到好方法,欢迎探讨。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值