简体   繁体   中英

How to prove the the correctness of recursive “search” algorithm in the code?

I don't know how to prove the recursive algorithm of the problem. I can't use the mathematical induction to solve the this proving.(although I am familiar with the mathematical induction).

The problem:

Given an array of integers nums and a positive integer k , find whether it's possible to divide this array into k non-empty subsets whose sums are all equal.

Example 1:

Input: nums = [4, 3, 2, 3, 5, 2, 1] , k = 4 Output: True Explanation: It's possible to divide it into 4 subsets (5), (1, 4), (2,3), (2,3) with equal sums.

Note:

1 <= k <= len(nums) <= 16. 0 < nums[i] < 10000.

The algorithm: ( https://leetcode.com/problems/partition-to-k-equal-sum-subsets/solution/ )

I first tried when i = 0 at the first recursion, the groups[i] = v , and I have to judge the search(groups, row, nums, target). However, at this time, I don't know how to think what the return value that is true or false will influence.

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);
    }

}

The method canPartitionKSubsets first computes the sum sum of all the numbers. If the partition existed then the sum of the elements in each partition must be target = sum//k . They check that sum is divisible by k .

They check if the last number is greater than target . If it were, then this number couldn't be in any group. So, they return false in that case.

Now it comes the search call. But lets make clear the interpretation of the variables. In each call to search the variable groups represent the sums of the numbers currently being considered added to each one of the k groups. The variable row represents the position in the original list of the number currently being considered to be added to one of the groups.

Inside search the loop all the cases of adding the number in position row to each one of the k groups. It adds it and recursively tries to search if the is a complete solution that way. If not, it removes it back from the group that it was added.

The groups are attempted to be filled with numbers from the list in order, stating from group 0 to group number k-1.

They break the loop when they reach a group that currently has sum zero. This is an error in the algorithm. For the statement as you wrote it. This step is here only to cut some loops, but it only makes sense under the assumption that all numbers are positive, which at least in your transcription is not given. If the problem allows non-positive numbers, just remove that line from the code.

The algorithm works simply because it tries all cases. If you arrange all possibilities of placing some of the numbers from the list into some of the k groups in a tree, starting with placing none as the root, and branching each time an additional number is placed, then the tree nodes are the same as stack calls, and the leaves of the tree are the arrangements in which all numbers have been placed. The algorithm is doing Depth First Search on the tree, except for the line

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

which is wrong for the problem as stated.

After reading above thought, I take a tumble. As we know, row represents the position in the original list of the number currently being considered to be added to one of the groups. If row < 0, we will get the all numbers added in the group.The try to put v in the suitable group. And the group in the bottom of the for loop at least have one element because all groups can be seen as the a set. If this group can't contain the one element, another can't, too. If the current group is not fit for the v, then try next group.

Note: The problem limit nums[i > 0. See the Note in the problem I edit. 1 <= k <= len(nums) <= 16. 0 < nums[i] < 10000.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM