我一直在努力解决问题我试图解决作为测试准备的一部分,我想我可以使用你的帮助。 我需要编写一个布尔方法,它接受带有整数的数组(正数和负数),如果数组可以拆分为两个等于组,则返回true,即每个组的数量等于另一组。 例如,对于这个数组:
int[]arr = {-3, 5, 12, 14, -9, 13};
该方法将返回true,因为-3 + 5 + 14 = 12 + -9 + 13。
对于这个数组:
int[]arr = {-3, 5, -12, 14, -9, 13};
该方法将返回false,因为即使-3 + 5 + 14 + -12 = -9 + 13,等式中每一边的数字量都不等于。
对于数组:
int[]arr = {-3, 5, -12, 14, -9};
该方法将返回false,因为数组长度不均匀。
方法必须是递归的,允许重载,每个辅助方法也必须递归,并且我不需要担心复杂性。
我一直试图解决这个问题三个小时,我甚至没有代码可以显示,因为我所做的所有事情远非解决方案。
如果某人至少可以给我一些伪代码,那就太棒了。
非常感谢!
答案 0 :(得分:4)
描述的问题是Partition问题的一个版本。首先请注意,您的公式相当于决定是否存在输入的子集,其总和高达所有元素总和的一半(需要为整数,否则实例无法求解,但这很容易校验)。基本上,在每个递归步骤中,要确定是否要将第一个数字选择到子集中,从而导致不同的递归调用。如果n
表示元素的数量,则必须选择n/2
(需要再次成为整数)。
让Sum
表示输入的总和,并假设续集中的Target := Sum / 2
为积分。如果我们让
f(arr,a,count) := true
if there is a subset of arr summing up to a with
exactly count elements
false
otherwise
我们获得以下递归
f(arr,a,count) = (arr[0] == a && count == 1)
||
(a == 0 && count == 0)
if arr contains only one element
f(arr\arr[0], a, count)
||
f(arr\arr[0], a - arr[0], count -1)
if arr contains more than one element
其中||
表示逻辑消除,&&
表示逻辑结合,\
表示删除元素。
非单例数组的两种情况对应于将arr
的第一个元素选择为所需子集或其相对补码。请注意,在实际实现中,a
实际上不会从数组中删除;一个起始索引,用作附加参数,将用0
初始化,并在每次递归调用中增加,最终到达数组的末尾。
最后,f(arr,Target,n/2)
会产生所需的值。
答案 1 :(得分:4)
你问过伪代码,但有时候把它写成Java也很简单明了。
此解决方案的一般想法是尝试将每个数字添加到等式的左侧或右侧。它跟踪递归中每一步的每一侧的计数和总和。评论中有更多解释:
class Balance {
public static void main(String[] args) {
System.out.println(balanced(-3, 5, 12, 14, -9, 13)); // true
System.out.println(balanced(-3, 5, -12, 14, -9, 13)); // false
}
private static boolean balanced(int... nums) {
// First check if there are an even number of nums.
return nums.length % 2 == 0
// Now start the recursion:
&& balanced(
0, 0, // Zero numbers on the left, summing to zero.
0, 0, // Zero numbers on the right, summing to zero.
nums);
}
private static boolean balanced(
int leftCount, int leftSum,
int rightCount, int rightSum,
int[] nums) {
int idx = leftCount + rightCount;
if (idx == nums.length) {
// We have attributed all numbers to either side of the equation.
// Now check if there are an equal number and equal sum on the two sides.
return leftCount == rightCount && leftSum == rightSum;
} else {
// We still have numbers to allocate to one side or the other.
return
// What if I were to place nums[idx] on the left of the equation?
balanced(
leftCount + 1, leftSum + nums[idx],
rightCount, rightSum,
nums)
// What if I were to place nums[idx] on the right of the equation?
|| balanced(
leftCount, leftSum,
rightCount + 1, rightSum + nums[idx],
nums);
}
}
}
这只是第一个想法的解决方案。它是O(2 ^ n),对于大n
来说显然相当慢,但是对于你给出的问题的大小来说这很好。
答案 2 :(得分:1)
您的策略应该是尽可能尝试所有组合。我将尝试记录我将如何实现这一目标。
注意我认为要求:使每个函数都使用递归有点困难,因为我会通过省略一些帮助函数来解决这个问题,这些函数使得代码更具可读性,所以在这种情况下我不会这样做。
通过递归,您总是希望向最终解决方案迈进,并检测您何时完成。所以我们在函数中需要两个部分:
我们的第一步中的一个技巧是,在获取集合的第一个元素后,如果我们尝试对剩余部分进行分区,我们不希望这两个集合相等,因为我们已经将第一个元素分配给其中一套。
这导致遵循此策略的解决方案:
public boolean isValidSet(MySet<int> inputSet, int sizeDifferenceSet1minus2)
{
if (inputSet.isEmpty())
{
return sizeDifferenceSet1minus2== 0;
}
int first = inptuSet.takeFirst();
return isValidSet(inputSet.copyMinusFirst(), sizeDifferenceSet1minus2+ first)
|| isValidSet(inputSet.copyMinusFirst(), sizeDifferenceSet1minus2+ -1 * first);
}
此代码需要一些您仍需要实现的帮助功能。
它的作用是首先测试我们是否已达到结束条件,如果是,则返回此分区是否成功。如果我们仍然在集合中留下了元素,我们会尝试如果将它添加到第一个集合中会发生什么,然后将其添加到第二个集合时会发生什么。请注意,我们实际上并没有跟踪集合,我们只是跟踪集合1减2之间的大小差异,减少(但你可以传递两个集合)。
另请注意,要使此实现起作用,您需要复制输入集而不是修改它!
对于一些背景信息:这个问题被称为Partition Problem,它以NP完全着称(这意味着它可能无法有效地解决大量输入数据,但它非常很容易验证分区确实是一个解决方案。
答案 3 :(得分:0)
这是一个详细的例子:
public static void main(String[] args)
{
System.out.println(balancedPartition(new int[] {-3, 5, 12, 14, -9, 13})); // true
System.out.println(balancedPartition(new int[] {-3, 5, -12, 14, -9, 13})); // false
System.out.println(balancedPartition(new int[] {-3, 5, -12, 14, -9})); // false
}
public static boolean balancedPartition(int[] arr)
{
return balancedPartition(arr, 0, 0, 0, 0, 0, "", "");
}
private static boolean balancedPartition(int[] arr, int i, int groupA, int groupB, int counterA, int counterB, String groupAStr, String groupBStr)
{
if (groupA == groupB && counterA == counterB && i == arr.length) // in case the groups are equal (also in the amount of numbers)
{
System.out.println(groupAStr.substring(0, groupAStr.length() - 3) + " = " + groupBStr.substring(0, groupBStr.length() - 3)); // print the groups
return true;
}
if (i == arr.length) // boundaries checks
return false;
boolean r1 = balancedPartition(arr, i + 1, groupA + arr[i], groupB, counterA + 1, counterB, groupAStr + arr[i] + " + ", groupBStr); // try add to group 1
boolean r2 = balancedPartition(arr, i + 1, groupA, groupB + arr[i], counterA, counterB + 1, groupAStr, groupBStr + arr[i] + " + "); // try add to group 2
return r1 || r2;
}
输出:
-3 + 5 + 14 = 12 + -9 + 13 // 第一个数组的一个选项
12 + -9 + 13 = -3 + 5 + 14 // 第一个数组的另一个选项
true // 对于第一个数组
false // 对于第二个数组
false // 第三个数组