我正在做某事,并且能够将问题简化为特定形式:给定k个整数中的每一个n个元组,例如:(a1,a2,a3,a4),(b1,b2,b3,b4 ),(c1,c2,c3,c4),(d1,d2,d3,d4),我希望选择任意数量的元组,将它们添加在一起时,给出的元组没有正数元素。如果选择元组a和b,则得到元组(a1 + b1,a2 + b2,a3 + b3,a4 + b4)。因此,如果a =(1,-2,2,0)且b =(-1,1,-3,0),则a + b =(0,-1,-1,0)不包含正数,因此是解决问题的方法。
除了检查所有子集元组的总和(需要2 ^ n个步骤)以外,是否有其他方法可以获取解决方案(或验证其不存在)?
由于这个问题来自我的脑海,而不是一本特定的教科书,所以我不知道表达它的正确方法,因此寻找答案的研究完全是徒劳的。我的大部分搜索都将我引向子总和问题,在该问题中,我们从列表中选择k个元素求和为一个特定问题。我的问题可以说是一个复杂的问题:我们从列表中选择一组元组,并且我们希望这些元组中每个元素的总和为<= 0。
编辑:由于提供了链接,并且由于注释表明难以实现小于指数的解决方案,因此为元素范围在-1,0到1之间的元组解决了该问题。对我来说就足够了此外,元组的范围为10,000-20,000个整数,并且不超过1000个元组。每个元组最多具有10个1和10 -1,其余为零
如果有人还能证明它是某种NP,那将是很棒的。
我无法提出DP解决方案,并且排序似乎没有用
答案 0 :(得分:1)
这可以使用动态规划在具有给定约束的伪多项式时间内解决。
这类似于subset sum problem的伪多项式时间动态规划解决方案。它仅扩展到多个维度(4)。
O(n * sum 4 ),或者在这种情况下,由于sum已被n
限制,
O(n 5 )
这是一个自顶向下的动态编程解决方案,带有C ++中的备注。
const int N = 50;
int a[50][4]= {{0, 1, -1, 0},
{1, -1, 0, 0},
{-1, -1, 0, -1}};
unordered_map<int, bool> dp[N];
bool subset(int n, int sum1, int sum2, int sum3, int sum4)
{
// Base case: No tuple selected
if (n == -1 && !sum1 && !sum2 && !sum3 && !sum4)
return true;
// Base case: No tuple selected with non-zero sum
else if(n == -1)
return false;
else if(dp[n].find(hashsum(sum1, sum2, sum3, sum4)) != dp[n].end() )
return dp[n][hashsum(sum1, sum2, sum3, sum4)];
// Include the current element
bool include = subset(n - 1,
sum1 - a[n][0],
sum2 - a[n][1],
sum3 - a[n][2],
sum4 - a[n][3]);
// Exclude the current element
bool exclude = subset(n - 1, sum1, sum2, sum3, sum4);
return dp[n][hashsum(sum1, sum2, sum3, sum4)] = include || exclude;
}
为便于记忆,哈希值的计算方式如下:
int hashsum(int sum1, int sum2, int sum3, int sum4) {
int offset = N;
int base = 2 * N;
int hashSum = 0;
hashSum += (sum1 + offset) * 1;
hashSum += (sum2 + offset) * base;
hashSum += (sum3 + offset) * base * base;
hashSum += (sum4 + offset) * base * base * base;
return hashSum;
}
然后,驱动程序代码可以搜索任何非正和,如下所示:
int main()
{
int n = 3;
bool flag = false;
int sum1, sum2, sum3, sum4;
for (sum1 = -n; sum1 <= 0; sum1++) {
for (sum2 = -n; sum2 <= 0; sum2++) {
for (sum3 = -n; sum3 <= 0; sum3++) {
for (sum4 = -n; sum4 <= 0; sum4++) {
if (subset(n - 1, sum1, sum2, sum3, sum4)) {
flag = true;
goto done;
}
}
}
}
}
done:
if (flag && (sum1 || sum2 || sum3 || sum4))
cout << "Solution found. " << sum1 << ' ' << sum2 << ' ' << sum3 << ' ' << sum4 << std::endl;
else
cout << "No solution found.\n";
return 0;
}
请注意,总之(0,0,0,0}的平凡解决方案始终存在,因此从来没有选择任何元素,因此在驱动程序代码中被忽略了。