LeetCode-698划分为k个相等的子集
题目描述:给定一个整数数组nums
和一个正整数k
,找出是否有可能把这个数组分成k
个非空子集,其总和都相等。
示例 :
输入: 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,则返回false,接下来遍历一遍数组,如果数组中有某个元素大于分组后每一组的和,则返回false
接下来进行的过程其实很简单,便是要将k-1个桶填满,此时可以用递归函数进行结局啊,该函数的功能是:从数组的第k位开始向后寻找,返回值是当前手中的桶的这一种情况是否满足问题的要求,即当前的情况和当前情况确定下来之后对之后的影响
如果说,当前已经填完了k-1个桶,则返回真
如果说,当前恰好填满了桶,则对下一个桶、下下个桶…直到填完之后的桶进行判断(此时可以认为是开新桶了,所以从0开始),如果说在当前的所有填满的桶确定的情况下之后的都能实现则返回真(递归之处在这里:大问题为检视包括当前的桶在内以及之后的桶的情况是否满足问题要求,小问题为之后的桶的情况是否满足要求,如果小问题返回值为真,则大问题的返回值也为真,如果小问题返回值为假,说明当前这个桶有不周到之处,此时也返回假)
如果说,当前没有把桶填满,则从当前桶的起始检视位置开始,向后进行检视,如果说遇到没有用到的数,则尝试将其放进桶中,这时候再用该函数对放了这个数之后的情形进行判断(递归之处在这里:大问题为检视包括当前的桶在内以及之后的桶的情况是否满足问题要求,小问题为检视把这个数放进去之后(所以研究小问题时起始检视位置为当前数的位置+1)当前及之后的桶的情况是否能符合要求,如果小问题返回为真,则说明当前这个数是可以放进去的,那么就保持该数为使用了的状态,并且将大问题返回真,如果说小问题返回假,则说明这个数不能放进去,那么就把它拿出来,继续在剩余的数组之中寻找能够使问题得到满足的数),如果说遍历完了一遍数组,都无法得到满足,则大问题,即检视包括当前的桶在内以及之后的桶的情况是否满足问题要求返回假
如果说觉得上面的陈述有些绕,可以这么想,你是一个人来做这个工作,你往桶中放元素,每想往桶中放某个元素,或者说判断桶满时可不可以把桶放下的时候,都可以去询问智者,放了这个元素之后之后的问题有没有可能成功,智者一族也不是无所不能的,他只能将这个元素先放进桶里面,然后进行判断,如果说元素放进去之后 桶炸了,他会直接告诉你不行。如果刚好分完了,他会直接告诉你行。如果刚好桶满了,他会拿着目前已经放下的桶去问智者的父亲,智者的父亲重复他的工作,并且告诉智者行不行,智者再告诉你行不行。如果说桶还没满,智者会从剩下没用过的元素里面一个个尝试,并且问他的智者行不行,他的父亲如果告诉他不行,则继续尝试,如果他的父亲一旦告诉他行,智者就会转告你行,如果找完了都不行,智者就会告诉你不行。(智者的父亲跟智者做的是同样的事)
代码实现如下:
bool flag [16]={}; //用于判断是否使用
bool IsPartition(int start ,int* a,int n, bool* flag,int k,int now,int target );
bool canPartitionKSubsets(int* a, int n, int k){
int sum=0;
for(int i=0;i<n;i++)
sum+=a[i];
for(int i=0;i<16;i++)
flag [i]=false;
if(k==0||sum%k!=0)
return false;
int bucket=sum/k;
for(int i=0;i<n;i++)
if(a[i]>bucket)
return false;
return IsPartition(0,a,n,flag,k,0,bucket);
}
bool IsPartition(int start ,int* a,int n, bool* flag,int k,int now,int target ){
if(k==1) //判断出剩下只有一个桶了,直接返回真
return true;
if(now==target) //当前桶刚好满,返回假定当前即之前桶可行的情况下后面是否可行
return IsPartition(0,a,n,flag,k-1,0,target);
else if(now>target) //桶爆了,返回假
return false;
for(int i=start;i<n;i++){ //寻找到一个可以放进去的
if(!flag[i]){
flag[i]=true;
if(IsPartition(i+1,a,n,flag,k,now+a[i],target)) //假定放进去之后后面的都可行则返回真
return true;
flag[i]=false;
}
}
return false; //都找不到返回假
}