[leetcode-递归]698. 划分为k个相等的子集

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

//======================================================================

int left = 0; //剩余期望之和
bool findSubSet(int * nums, int numsSize, int target, int * visit)
{
    //如果期望值为0,则说明已经找齐元素,返回true
    if (0 == target)
    {
        return true;
    }

    //剩余期望值为left
    left = target;
    
    //同步遍历数组和访问标记
    for (int i = 0; i < numsSize; i++)
    {
        //如果访问标记为1,则已经访问过,直接跳过
        if (1 == visit[i])
        {
            continue;
        }

        //剩余期望值为0,说明已找齐元素,返回true
        if (left == 0)
        {
            return true;
        }

        //如果剩余期望值小于当前元素值,则跳过
        if (left - nums[i] < 0)
        {
            continue;
        }

        //如果剩余期望值等于当前元素值,则找齐元素,返回true
        if (left - nums[i] == 0)
        {
            visit[i] = 1; //修改元素访问标记
            left = 0;     //剩余期望值为0
            return true;
        }

        //当前元素访问标记置1
        visit[i] = 1;
        
        //继续查找剩余期望值需要的元素,left - nums[i] 为剩余期望值
        bool res = findSubSet(nums, numsSize, left - nums[i], visit);
        
        //如果剩余期望值没找到,重置该元素访问标记为0,说明已访问的找个元素组合不合适,需要恢复剩余期望值和访问标记,继续后续的遍历
        if (false == res)
        {
            visit[i] = 0;
            left = left+nums[i];
        }

        //如果剩余期望值找到,则返回true
        else
        {
            left = 0;
            return true;
        }

        
    }

    //如果遍历结束,找不到期望的元素,则返回false
    return false;
}

//从大到小排序
int compare(const void * src, const void * dst)
{
    int i = *(int *)src;
    int j = *(int *)dst;
    if (i > j)
    {
        return -1;
    }

    if (i == j)
    {
        return 0;
    }
    
    return 1;
}

bool canPartitionKSubsets(int* nums, int numsSize, int k) {
    //特殊条件
    if (NULL == nums || numsSize<1 || k<1)
    {
        return false;
    }

    //计算元素之和
    int sum = 0;
    for (int i = 0; i < numsSize; i++)
    {
        sum += nums[i];
    }

    //如果不划分k个子集,则返回false
    if (0 != sum % k)
    {
        return false;
    }

    //对数组元素排序,从到到小
    qsort(nums, numsSize, sizeof(int), compare);
    
    //构建访问标记数组
    //int * visit = (int *)malloc(sizeof(int) * numsSize);
    //memset(visit, 0x0, sizeof(int) * numsSize);
    int visit[numsSize];
    for(int i=0; i<numsSize; ++i)
    {
        visit[i]=0;
    }

    //k个子集中每个子集的计算期望和
    int target = sum /k;

    //因为每个元素大于0,如果出现元素大于期望之和,则返回false
    for (int i = 0; i < numsSize; i++)
    {
        if (nums[i] > target)
        {
            return false;
        }
    }

    //划分k个子集,划分k次
    for (int i = 0; i < k; i++)
    {
        //如果一次false,则直接返回false
        if (false == findSubSet(nums, numsSize, target, visit))
        {
            return false;
        }
    }

    //k个子集全部划分成功,则返回true
    return true;
}

猜你喜欢

转载自blog.csdn.net/qq_20398345/article/details/81103193