如何在代码中证明递归“搜索”算法的正确性?

时间:2019-09-17 12:09:00

标签: java algorithm recursion

我不知道如何证明问题的递归算法。我不能用数学归纳法来解决这个问题。(尽管我对数学归纳法很熟悉)。

问题:

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

算法:   (https://leetcode.com/problems/partition-to-k-equal-sum-subsets/solution/

我第一次尝试i = 0的第一次递归是groups[i] = v时,必须判断搜索(组,行,数字,目标)。但是,此时,我不知道该如何思考返回true或false会对返回值产生什么影响。

class Solution {
    public boolean search(int[] groups, int row, int[] nums, int target) {
        if (row < 0) return true;
        int v = nums[row--];
        for (int i = 0; i < groups.length; i++) {
            if (groups[i] + v <= target) {
                groups[i] += v;
                if (search(groups, row, nums, target)) return true;
                groups[i] -= v;
            }
            if (groups[i] == 0) break;
        }
        return false;
    }

    public boolean canPartitionKSubsets(int[] nums, int k) {
        int sum = Arrays.stream(nums).sum();
        if (sum % k > 0) return false;
        int target = sum / k;

        Arrays.sort(nums);
        int row = nums.length - 1;
        if (nums[row] > target) return false;
        while (row >= 0 && nums[row] == target) {
            row--;
            k--;
        }
        return search(new int[k], row, nums, target);
    }

}

2 个答案:

答案 0 :(得分:0)

方法canPartitionKSubsets首先计算所有数字的总和sum。 如果存在分区,则每个分区中元素的总和必须为target = sum//k。他们检查sum是否可被k整除。

他们检查最后一个数字是否大于target。如果是这样,则此数字不能在任何组中。因此,在这种情况下,他们返回false

现在是搜索电话了。但是,让我们弄清楚变量的解释。 在对search的每次调用中,变量groups代表当前考虑添加到k组中的每个组中的数字的总和。变量row代表当前被认为要添加到组之一中的数字在原始列表中的位置。

search循环中,所有将row组中的数字加到k组中的每个组的情况。它添加了它,然后递归地尝试搜索,这样是否是一个完整的解决方案。如果不是,则会将其从添加的组中删除。

尝试按顺序用列表中的数字填充组,从组0到组号k-1。

当他们到达当前总和为零的组时,他们打破了循环。这是算法中的错误。对于您编写的声明。这一步仅是为了减少一些循环,但只有在所有数字均为正数的假设下才有意义,至少在您的抄录中没有给出。如果问题允许使用非正数,则只需从代码中删除该行即可。

该算法之所以简单工作是因为它会尝试所有情况。如果您安排了将列表中的一些数字放入树中的某些k组中的所有可能性,从不将其作为根开始,并在每次放置其他数字时分支,则树节点与堆栈调用相同,并且树的叶子是所有数字都放在其中的排列。该算法在树上进行深度优先搜索,除了直线

if (groups[i] == 0) break;

对于所述问题,这是错误的。

答案 1 :(得分:0)

阅读以上思想后,我大跌眼镜。 众所周知,row代表当前被认为要添加到其中一个组中的号码在原始列表中的位置。如果row <0,我们将获得组中添加的所有数字。尝试将v放入合适的组中。并且for循环底部的组至少具有一个元素,因为所有组都可以看作一组。如果该组不能包含一个元素,则另一个也不能。如果当前组不适合v,请尝试下一个组。

注意:问题限制数字[i>0。请参阅我编辑的问题中的注意。 1 <= k <= len(nums)<=16。0