元组非指数算法中不同元素的总和

时间:2019-06-02 08:46:04

标签: algorithm tuples

我正在做某事,并且能够将问题简化为特定形式:给定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解决方案,并且排序似乎没有用

1 个答案:

答案 0 :(得分:1)

这可以使用动态规划在具有给定约束的伪多项式时间内解决。

说明

这类似于subset sum problem的伪多项式时间动态规划解决方案。它仅扩展到多个维度(4)。

时间复杂度

O(n * sum 4 ,或者在这种情况下,由于sum已被n限制, O(n 5

解决方案

Demo

这是一个自顶向下的动态编程解决方案,带有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}的平凡解决方案始终存在,因此从来没有选择任何元素,因此在驱动程序代码中被忽略了。