如何将一个集合分为两个子集,使得两个集合中的数字之和之间的差异最小?
给定一组数字,将这些数字分为两个子集,使两个子集中的数字之和之间的差异最小。
这是我的想法,但我不确定这是否是正确的解决方案:
- 对数组进行排序
- 取前 2 个元素。将它们视为 2 个集合(每个集合有 1 个元素)
- 从数组中取出下一个元素。
- 决定该元素应该进入哪个集合(通过计算总和 => 它应该是最小值)
- 重复
这是正确的解决方案吗?我们可以做得更好吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(20)
您所描述问题的决策版本是NP-complete 问题,称为 分区问题。有许多近似值,在许多情况下提供了最佳的或至少足够好的解决方案。
您描述的简单算法是游乐场孩子们选择团队的一种方式。如果集合中的数字具有相似的数量级,则此贪婪算法表现得非常好。
文章 最简单最难的问题,作者是美国科学家,对该问题进行了精彩的分析。你应该仔细阅读它!
The decision version of the problem you are describing is an NP-complete problem and it is called the partition problem. There are a number of approximations which provide, in many cases, optimal or, at least, good enough solutions.
The simple algorithm you described is a way playground kids would pick teams. This greedy algorithm performs remarkably well if the numbers in the set are of similar orders of magnitude.
The article The Easiest Hardest Problem, by American Scientist, gives an excellent analysis of the problem. You should go through and read it!
不,那不行。不存在多项式时间解(除非 P=NP)。您能做的最好的事情就是查看所有不同的子集。看一下子集和问题。
考虑列表
[0, 1, 5, 6]
。您将声明{0, 5}
和{1, 6}
,而最佳答案实际上是{0, 1, 5}
并且<代码>{6}。No, that doesn't work. There is no polynomial time solution (unless P=NP). The best you can do is just look at all different subsets. Have a look at the subset sum problem.
Consider the list
[0, 1, 5, 6]
. You will claim{0, 5}
and{1, 6}
, when the best answer is actually{0, 1, 5}
and{6}
.不,你的算法是错误的。你的算法遵循贪婪的方法。
我实现了你的方法,但在这个测试用例中失败了:
(您可以尝试此处)
贪心算法:
测试用例:
贪心算法失败的原因是它没有考虑当前较大和集中取较大元素而稍后在较大和集中取较小元素可能会产生更好结果的情况。它总是尝试最小化当前差异,而不探索或了解进一步的可能性,而在正确的解决方案中,您可能会在更大的集合中包含一个元素,并稍后包含一个更小的元素来补偿这种差异,与上面的测试用例相同。
正确的解决方案:
要理解该解决方案,您需要按顺序理解以下所有问题:
我的代码(与这个):
No, Your algorithm is wrong. Your algo follows a greedy approach.
I implemented your approach and it failed over this test case:
(You may try here)
A greedy algorithm:
Test case:
The reason greedy algorithm fails is that it does not consider cases when taking a larger element in current larger sum set and later a much smaller in the larger sum set may result much better results. It always try to minimize current difference without exploring or knowing further possibilities, while in a correct solution you might include an element in a larger set and include a much smaller element later to compensate this difference, same as in above test case.
Correct Solution:
To understand the solution, you will need to understand all below problems in order:
My Code (Same logic as this):
组合之上的组合方法:
Combinations over combinations approach:
递归方法是从数组的所有值生成所有可能的和并检查
哪种解决方案是最优的。
为了生成总和,我们要么将第 i 个项目包含在集合 1 中,要么不包含,即包含在
集合2。
时间和空间的时间复杂度都是O(n*sum)。T
The recursive approach is to generate all possible sums from all the values of array and to check
which solution is the most optimal one.
To generate sums we either include the i’th item in set 1 or don’t include, i.e., include in
set 2.
The time complexity is O(n*sum) for both time and space.T
我们可以使用动态规划(类似于我们发现一个集合是否可以划分为两个等和子集的方法)。然后我们找到最大可能的总和,这将是我们的第一个分区。
第二个分区将是总和与第一个总和的差。
答案将是第一分区和第二分区的差异。
We can use Dynamic Programming (similar to the way we find if a set can be partitioned into two equal sum subsets). Then we find the max possible sum, which will be our first partition.
Second partition will be the difference of the total sum and firstSum.
Answer will be the difference of the first and second partitions.
也许你正在谈论这个。
<一href="https://practice.geeksforgeeks.org/problems/partition-a-set-into-two-subsets-such-that-the-difference-of-subset-sums- is-minimum-set-2/1?page=3&difficulty%5B%5D=2&status%5B%5D=unsolved&status%5B%5D=attempted&sortBy=difficulty" rel="nofollow noreferrer">https://practice.geeksforgeeks.org/problems/partition-a-set-into-two-subsets-such-that-the-difference-of-subse t-sums-is-minimum-set-2/1?page=3&difficulty[]=2&status[]=unsolved&status[]=attempted&sortBy=difficulty
我使用“中间相遇”的方法来做到这一点。它是一种优化技术,其工作原理是将数组分成两部分,然后生成子集。
代码,如有疑问请随时询问
CODE BY RAINX, ABHIJIT ROY, NIT AGARTALA
Perhaps you are talking about this.
https://practice.geeksforgeeks.org/problems/partition-a-set-into-two-subsets-such-that-the-difference-of-subset-sums-is-minimum-set-2/1?page=3&difficulty[]=2&status[]=unsolved&status[]=attempted&sortBy=difficulty
I did it using MEET IN THE MIDDLE APPROACH. Its an optimization technique which works by splitting the array in two parts and then generating the subsets.
Code , feel free to ask if any queries
CODE BY RAINX, ABHIJIT ROY, NIT AGARTALA
一个小改变:颠倒顺序——从最大的数字开始,然后依次递减。这将最大限度地减少错误。
One small change: reverse the order - start with the largest number and work down. This will minimize the error.
您是否将子集按降序或升序排序?
想想看,数组 {1, 3, 5, 8, 9, 25}
如果你要除法,你会得到 {1,8,9} =18 {3,5,25} =33
如果它按降序排序,效果会更好
{25,1}=26 {9,8,5,3}=25
所以你的解决方案基本上是正确的,它只需要确保首先取最大值即可。
编辑:阅读 tskuzzy 的帖子。我的不工作
Are you sorting your subset into decending order or ascending order?
Think about it like this, the array {1, 3, 5, 8, 9, 25}
if you were to divide, you would have {1,8,9} =18 {3,5,25} =33
If it were sorted into descending order it would work out a lot better
{25,1}=26 {9,8,5,3}=25
So your solution is basically correct, it just needs to make sure to take the largest values first.
EDIT: Read tskuzzy's post. Mine does not work
这是背包和子集和问题的变体。
在子集和问题中,给定n个正整数和一个值k,我们必须找到值小于或等于k的子集的和。
在上面的问题中,我们给定了一个数组,这里我们必须找到总和小于或等于total_sum(数组值的总和)的子集。
所以
可以使用背包算法的变体找到子集和,通过
将利润作为给定的数组值。最终的答案是
总和-dp[n][总和/2]。看一下下面的代码就清楚了
理解。
This is a variation of the knapsack and subset sum problem.
In subset sum problem, given n positive integers and a value k and we have to find the sum of subset whose value is less than or equal to k.
In the above problem we have given an array, here we have to find the subset whose sum is less than or equal to total_sum(sum of array values).
So the
subset sum can be found using a variation in knapsack algorithm,by
taking profits as given array values. And the final answer is
total_sum-dp[n][total_sum/2]. Have a look at the below code for clear
understanding.
这可以使用 BST 来解决。
首先对数组进行排序,比如 arr1
要开始使用 arr1 的最后一个元素创建另一个 arr2 (从 arr1 中删除该元素)
现在:重复这些步骤,直到没有交换发生。
即现在检查 arr2 中是否有任何元素可以移动到 arr1
Java 代码示例:
This can be solve using BST.
First sort the array say arr1
To start create another arr2 with the last element of arr1 (remove this ele from arr1)
Now:Repeat the steps till no swap happens.
i.e. now check if we have any element in arr2 which can be moved to arr1
Sample Java Code:
您可以使用位来解决此问题,方法是使用位循环遍历所有可能的组合:
主要算法:
使用 long long 以获得更大的输入。
但在这里我找到了一个递归和动态编程解决方案,我使用这两种方法来解决问题,并且都可以完美地获得更大的输入。希望这有帮助:) 解决方案链接
you can use bits to solve this problem by looping over all the possible combinations using bits:
main algorithm:
use long long for greater inputs.
but here I found a recursive and dynamic programming solution and I used both the approaches to solve the question and both worked for greater inputs perfectly fine. Hope this helps :) link to solution
如果差异是总和的差值小于较大数组(总和较大的数组)的最小数量,则将元素从较大数组移动到较小数组。移动发生的条件是,较大数组中的元素的值小于或等于差值。当较大数组中的所有元素都大于差值时,移位停止并发生交换。我只是交换数组的最后一个元素(通过查找要交换的两个元素可以提高效率),但这仍然有效。如果这个逻辑在任何情况下失败,请告诉我。
}
If the difference is difference of sum is less than the minimum number of the larger array(array with bigger sum), then shift the elements from the bigger array to smaller array.Shifting happens with the condition, that element from the bigger array with value less than or equal to the difference.When all the elements from the bigger array is greater than the difference, the shifting stops and swapping happens. I m just swapping the last elements of the array (It can be made more efficient by finding which two elements to swap), but still this worked. Let me know if this logic failed in any scenario.
}
这里有一个可能的解决方案 - https://stackoverflow.com/a/31228461/4955513
这个 Java 程序似乎可以解决这个问题,只要满足一个条件——该问题只有一个解决方案。
A possible solution here- https://stackoverflow.com/a/31228461/4955513
This Java program seems to solve this problem, provided one condition is fulfilled- that there is one and only one solution to the problem.
相同的Java代码
Java code for the same
这是递归方法
这是一种自上而下的动态方法来降低时间复杂度
Here is recursive approach
Here is a Top down Dynamic approach to reduce the time complexity
}
使用 SumI =0 和 SumE= a 中所有元素的总和来调用该函数。
这个 O(n!) 解决方案确实计算了我们将给定数组分为两部分的方式,以使差异最小。
但由于 n! 绝对不实用。希望使用 DP 来改善时间复杂度。
}
call the function with SumI =0 and SumE= sumof all the elements in a.
This O(n!) solution does compute the way we can divide the given array into 2 parts such the difference is minimum.
But definitely not practical due to the n! time complexity looking to improve this using DP.
许多答案提到在非常可接受的时间内获得“近似”解决方案。但既然是在面试中被问到的,我不认为他们需要近似算法。我也不认为他们需要一个朴素的指数算法。
谈到这个问题,假设数和的最大值已知,实际上可以使用动态规划在多项式时间内求解。参考这个链接
https://people.cs.clemson.edu/~bcdean/dp_practice/dp_4 .swf
Many answers mentioned about getting an 'approximate' solution in a very acceptable time bound . But since it is asked in an interview , I dont expect they need an approximation algorithm. Also I dont expect they need a naive exponential algorithm either.
Coming to the problem , assuming the maximum value of sum of numbers is known , it can infact be solved in polynomial time using dynamic programming. Refer this link
https://people.cs.clemson.edu/~bcdean/dp_practice/dp_4.swf