【发布时间】:2020-12-11 03:03:46
【问题描述】:
考虑一个数组arr。
s1 和 s2 是我们原始数组 arr 的任意两个子集。我们应该对原始数组进行分区,使这两个子集具有最小差异,即min(sum(s1) - sum(s2)),其中sum(s1) >= sum(s2)。
我应该打印这两个子集,即s1 和s2,作为我的输出。
您可以将这些子集存储在向量中。
例如如果arr[] = [0,1,5,6],那么最小的区别是0,我的两个子集是s1[] = [0,1,5]和 s2[] = [6]。
另一个例子可以是arr[] = [16,14,13,13,12,10,9,3],两个子集是s1[]=[16,13,13,3]和s2[] = [14,12,10,9],最小差值又是0。
我似乎不知道如何到达这两个子集。
非常感谢!
注意:我知道如何使用 DP 获得两个子集的最小差异,但我无法继续进行。它得到了我无法做到的两个子集(差异很小)。 只需一个沿着正确方向轻推的算法就可以了。
#include<iostream>
#include<vector>
using namespace std;
int min_subset_sum_diff(int a[], int n,int sum) {
vector<vector<bool>> go(n + 1, vector<bool>(sum + 1, false));
for (int i = 0; i < n + 1; ++i) {
go[i][0] = true;
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= sum; ++j) {
if (a[i - 1] <= j) {
go[i][j] = go[i - 1][j - a[i - 1]] || go[i - 1][j];
}
else {
go[i][j] = go[i - 1][j];
}
}
}
for (int j = (sum / 2); j >= 0; --j) {
if (go[n][j]) { // only need the last row as I need all the elements, which is n
return sum - 2 * j;
}
}
return INT_MAX;
}
int main() {
int a[] = { 3,100,4,4 };
int n = sizeof(a) / sizeof(a[0]);
int sum = 0;
for (auto i : a) {
sum += i;
}
cout << min_subset_sum_diff(a, n,sum);
}
s1 = sum(first_subset)
s2= sum(second_subset)
假设s2>=s1,
s1 + s2 = sum_arr
s2 = sum_arr - s1 //using this in the next equation
s2-s1 = difference
(sum_arr - s1) - s1 = difference
sum_arr - 2*s1 = difference
这是我的基本逻辑。
sum(s1) 和 sum(s2) 介于 0..sum_arr 之间
由于我假设s1 < s2,s1 的值只能在0..(sum_arr/2) 之间
我的目标是sum_arr - 2*s1 的最小值,这对于最大的s1 来说是正确的
【问题讨论】:
-
如果您知道如何通过 DP 获得最小差异,您只需要记住分区以及您计算的最小差异
-
@user123456 可能是因为看起来可能是家庭作业,以前有人问过,像geeksforgeeks.org/… 这样的解决方案很容易通过 Google 搜索。
-
这实际上是一个技术性的 F2F 面试问题。而且我还没有找到任何将数组实际划分为两个子数组的解决方案。所有解决方案仅输出最小差异。在这里问之前我做了研究。
-
@user123456 请参阅stackoverflow.com/a/63362010/585411 了解我对此问题的变体的解决方案。 (找到总和为给定总和的所有固定大小的子集。没有重复。)如果你能理解我在那里使用链表的方式,那么解决这个问题应该很容易。当然,它只会输出总和为二分之一的集合,您必须返回并找出该集合的补码。
-
你不应该问问题,删除它,因为你不喜欢 cmets 并在一小时后问完全相同的问题。
标签: c++ arrays algorithm subset dynamic-programming