如何解决在集合中找到整数符号的组合使得集合总和为 0 的算法
给定一组 S n 个正整数,我们想知道是否可以找到一个组合S 中每个数字的符号(+ 或 -),使得 S 的和为 0。
如何有效地解决这个问题?基于类似的问题,我想某种动态编程是有序的。有没有关于这个特定问题的文献(我找不到它)。
我想这类似于子集求和问题。但是,现在我们必须使用整个集合,并且对于每个整数 si 我们可以包含 -si 或 +si,但不能同时使用。
解决方法
这个问题的解决方案涉及子集和问题。
如果有办法求和到数组总和的一半,那么我们可以将所有这些数字设置为负数。其余的数字将为正数。由于这些子集中的每一个总和为总和的一半,因此它们各自的总和将为 0。
C++代码如下:
#include<stdio.h>
int arr[] = {1,2,3,4};
int n = 5; // size of arr
int sum = 0;
// dp array only needs to be [n + 1][total sum + 1] big
bool dp[30][100];
inline void subset_sum(){
for (int i = 0; i <= sum; i++)
dp[0][i] = false;
for (int i = 0; i <= n; i++)
dp[i][0] = true;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= sum; j++) {
dp[i][j] = dp[i - 1][j];
if (arr[i - 1] <= j)
dp[i][j] |= dp[i - 1][j - arr[i - 1]];
}
}
}
int main(){
for (int i = 0; i < n; i++)
sum += arr[i];
// run subset sum dp using a bottom-up approach
// True = sum is possible,False = not possible
subset_sum();
int max_half;
for (int i = sum / 2; i>=1; i--){
if (dp[n][i]){ // it is possible to sum to i using values in arr
max_half = i;
break;
}
}
// output will be the closest sum of positives
// and negatives to 0
printf("%d\n",2 * max_half - sum);
return 0;
}
此代码的输出将是集合中正数和负数组合的最接近的可能总和为 0。
2 * max_half - sum
可以从 max_half - (sum - max_half)
推导出来,这将是我们最好的总和减去其余数字。
以下是不同数字集及其各自输出的一些示例:
设置:{1,4}
,输出:0
。
设置:{1,1,1}
,输出:-1
。
设置:{5,6,8,9,2}
,输出:0
。
设置:{1,50}
,输出:-49
。
子集求和问题网上有many explanations,这里就不解释了。
这段代码的时间复杂度为O(n * sum),空间复杂度为O(n * sum)。
也可以通过使用一维 dp 数组来牺牲一些时间复杂度来提高空间复杂度。
,鉴于问题似乎是 NP 完全的, 使用 SAT、MILP、CP 或 ASP 求解器是最佳选择, 因为它们是为解决此类问题而量身定制的。
解决方案
这是一个使用 ASP(答案集编程)的解决方案。
给定文件 instance.lp
:
value(12).
value(12).
value(1).
value(2).
value(3).
value(5).
value(6).
value(7).
和文件 encoding.lp
:
% every value can be positive (or not)
{pos(X)} :- value(X).
% fail if the sum is not 0
:- not 0 = #sum {V : pos(V); -V : not pos(V),value(V)}.
% format output
#show pos/1.
#show neg(V) : not pos(V),value(V).
问题可以用clingo解决, potassco 工具集的 ASP 求解器(可通过 conda、pip、Ubuntu Package Manger 等轻松安装...)。
调用:
clingo instance.lp encoding.lp
给你结果:
Answer: 1
pos(1) pos(2) pos(3) pos(5) pos(7) neg(6) neg(12)
您可以列举所有可能的解决方案:
clingo instance.lp encoding.lp 0
给你
Answer: 1
pos(1) pos(2) pos(3) pos(5) pos(7) neg(6) neg(12)
Answer: 2
pos(2) pos(3) pos(6) pos(7) neg(5) neg(1) neg(12)
Answer: 3
pos(5) pos(6) pos(7) neg(3) neg(2) neg(1) neg(12)
Answer: 4
pos(12) pos(1) pos(2) pos(3) neg(7) neg(6) neg(5)
Answer: 5
pos(12) pos(6) neg(7) neg(5) neg(3) neg(2) neg(1)
Answer: 6
pos(12) pos(1) pos(5) neg(7) neg(6) neg(3) neg(2)
ASP
使用ASP解决问题的好处在于:
- 易于维护(对问题的描述非常简短,易于阅读)
- 非常快(基于 SAT 和 CDNL)
- 声明式(你只描述问题,而不是如何解决它)
- 易于扩展,具有其他约束
- 还能够进行各种优化(例如优化最大的子集以形成总和)
编辑
你也可以复制粘贴这两个文件的内容,自己在线查看结果,使用clingo
here
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。