【问题标题】:3-PARTITION problem3-PARTITION 问题
【发布时间】:2023-03-04 23:27:01
【问题描述】:

这是另一个动态编程问题 (Vazirani ch6)

考虑以下 3-PARTITION 问题。给定整数 a1...an,我们 想确定是否是 可以将 {1...n} 划分为 三个不相交的子集 I, J, K 这样 那个

sum(I) = sum(J) = sum(K) = 1/3*sum(ALL)

例如,对于输入 (1; 2; 3; 4; 4; 5个; 8) 答案是肯定的,因为有 是分区 (1; 8), (4; 5), (2; 3; 4)。另一方面,对于输入 (2; 2; 3; 5) 答案是否定的。设计 并分析动态规划 运行在 3-PARTITION 的算法 n 和 (Sum a_i) 中的时间多项式

我该如何解决这个问题?我知道 2-partition 但仍然无法解决它

【问题讨论】:

  • 这不是(或至少 2 个分区)NP-complete 吗?
  • 对于任意大小的整数;是的。复制粘贴的问题可能会在runs in time poly- nomial in n and 之后提到该限制,我想。
  • 您遗漏了一个关键部分(我添加了它)。它是 n 和 a_i 中的多项式。如果不包括总和 a_i,则问题实际上是 NP-Hard。

标签: algorithm dynamic-programming partition-problem


【解决方案1】:

创建一个三维数组,其中size是元素个数,part等于所有元素的总和除以3。所以数组的每个单元[ seq][sum1][sum2] 告诉您是否可以使用给定数组 A[] 中的最大 seq 元素创建 sum1 和 sum2。所以计算数组的所有值,结果将在单元格数组中[使用所有元素][所有元素的总和 / 3][所有元素的总和 / 3],如果你可以创建两个集合而不交叉等于 sum/3,那么将是第三组。

检查逻辑:将A[seq]元素排除到第三个和(未存储),检查没有元素的单元格是否具有相同的两个和;或包含到 sum1 - 如果可以在没有 seq 元素的情况下获得两个集合,其中 sum1 小于元素 seq A[seq] 的值,并且 sum2 不会更改;或者像以前一样包含到 sum2 检查。

int partition3(vector<int> &A)
{
    int part=0;
    for (int a : A)
        part += a;
    if (part%3)
        return 0;
    int size = A.size()+1;
    part = part/3+1;
    bool array[size][part][part];
    //sequence from 0 integers inside to all inside
    for(int seq=0; seq<size; seq++)
        for(int sum1=0; sum1<part; sum1++)
            for(int sum2=0;sum2<part; sum2++) {
                bool curRes;
                if (seq==0)
                    if (sum1 == 0 && sum2 == 0)
                        curRes = true;
                    else
                        curRes= false;
                else {
                    int curInSeq = seq-1;
                    bool excludeFrom = array[seq-1][sum1][sum2];
                    bool includeToSum1 = (sum1>=A[curInSeq]
                                          && array[seq-1][sum1-A[curInSeq]][sum2]);
                    bool includeToSum2 = (sum2>=A[curInSeq]
                                          && array[seq-1][sum1][sum2-A[curInSeq]]);
                    curRes = excludeFrom || includeToSum1 || includeToSum2;
                }
                array[seq][sum1][sum2] = curRes;
            }
    int result = array[size-1][part-1][part-1];
    return result;
}

【讨论】:

    【解决方案2】:

    正如我在另一个类似的问题中回答的那样,C++ 实现看起来像这样:

    int partition3(vector<int> &A)
    {
      int sum = accumulate(A.begin(), A.end(), 0);
      if (sum % 3 != 0)
      {
        return false;
      }
      int size = A.size();
    
      vector<vector<int>> dp(sum + 1, vector<int>(sum + 1, 0));
      dp[0][0] = true;
    
      // process the numbers one by one
      for (int i = 0; i < size; i++)
      {
        for (int j = sum; j >= 0; --j)
        {
          for (int k = sum; k >= 0; --k)
          {
            if (dp[j][k])
            {
              dp[j + A[i]][k] = true;
              dp[j][k + A[i]] = true;
            }
          }
        }
      }
      return dp[sum / 3][sum / 3];
    }
    

    【讨论】:

      【解决方案3】:

      您真的想要 Korf 的完整 Karmarkar-Karp 算法(http://ac.els-cdn.com/S0004370298000861/1-s2.0-S0004370298000861-main.pdfhttp://ijcai.org/papers09/Papers/IJCAI09-096.pdf)。给出了对三分区的概括。考虑到问题的复杂性,该算法速度惊人,但需要一些实现。

      KK的本质思想是确保大小相似的大块出现在不同的分区中。一组成对的块,然后可以将其视为较小的块,其大小等于可以正常放置的大小差异:通过递归执行此操作,最终得到易于放置的小块。然后对块组进行两种着色,以确保处理相反的放置。 3-partition 的扩展有点复杂。 Korf 扩展是在 KK 顺序中使用深度优先搜索来找到所有可能的解决方案或快速找到解决方案。

      【讨论】:

        【解决方案4】:

        假设您要将集合 $X = {x_1, ..., x_n}$ 分区到 $k$ 分区中。 创建一个 $ n \times k $ 表。假设成本$M[i,j]$ 是$j$ 分区中$i$ 元素的最大总和。只需递归地使用以下最优性标准来填充它:

        M[n,k] = min_{i\leq n}  max ( M[i, k-1], \sum_{j=i+1}^{n} x_i ) 
        
        Using these initial values for the table: 
        
        M[i,1] = \sum_{j=1}^{i} x_i  and  M[1,j] = x_j  
        
        The running time is $O(kn^2)$ (polynomial )
        

        【讨论】:

          【解决方案5】:

          我认为通过减少它是这样的:

          将 2-partition 减少到 3-partition:

          令 S 为原始集合,A 为其总和,则令 S'=union({A/2},S)。 因此,对集合 S' 执行 3 分区会产生三个集合 X、Y、Z。 X、Y、Z中,必须有一个是{A/2},说是集合Z,那么X和Y就是一个2-partition。 S'上3-partition的witnesses就是S上2-partition的witnesses,因此2-partition归约为3-partition。

          【讨论】:

            【解决方案6】:

            如果这个问题是可以解决的;那么sum(ALL)/3 必须是一个整数。任何解决方案都必须有SUM(J) + SUM(K) = SUM(I) + sum(ALL)/3。这代表了对concat(ALL, {sum(ALL)/3}) 的 2 分区问题的解决方案。

            你说你有一个 2 分区的实现:用它来解决 那个 问题。然后(至少)两个分区之一将包含数字sum(ALL)/3 - 从该分区中删除数字,您会找到I。对于另一个分区,再次运行 2-partition,将JK 分开;毕竟JK 本身的总和必须相等。

            编辑: 这个解决方案可能不正确 - 连接集的 2 分区将有多个解决方案(I、J、K 中的每个至少一个) - 但是,如果有其他解决方案,那么“另一边”可能不是由 I、J、K 中的两个的并集组成,并且可能根本不可拆分。你需要真正思考,我担心:-)。

            尝试 2: 迭代多重集,保持以下映射:R(i,j,k) :: Boolean 这表示直到当前迭代,数字是否允许划分为三个多重集总和为ijk。即,对于任何R(i,j,k) 和下一个数字n 在下一个状态R' 它保持R'(i+n,j,k)R'(i,j+n,k)R'(i,j,k+n)。请注意,复杂性(根据 excersize)取决于输入数字的 幅度;这是一个伪多项式时间算法。 Nikita 的解决方案在概念上相似,但比此解决方案更有效,因为它不跟踪第三组的总和:这是不必要的,因为您可以简单地计算它。

            【讨论】:

              【解决方案7】:

              很容易将 2 套解决方案推广到 3 套案例。

              在原始版本中,您创建布尔数组 sums ,其中 sums[i] 告诉 sum i 是否可以使用集合中的数字达到。然后,一旦创建了数组,您只需查看sums[TOTAL/2] 是否为true

              既然你说你已经知道旧版本,我就只描述它们之间的区别。

              在 3 分区的情况下,您保留布尔数组 sums,其中 sums[i][j] 告诉第一组是否可以有 sum i 和第二个 - sum j。然后,一旦创建了数组,您只需查看 sums[TOTAL/3][TOTAL/3] 是否为 true

              如果原始复杂度是O(TOTAL*n),这里是O(TOTAL^2*n)
              它可能不是严格意义上的多项式,但原始版本也不是严格的多项式:)

              【讨论】:

              • 如果有负数,复杂度比你说的要高一点,但这是一个细节。
              • @st0le sums[i][j] 告诉我们是否可以将原始集合分成 3 个子集,首先是 sum i,第二个是 sum j(第三个,显然是 sum TOTAL - i - j)。有帮助吗?
              • @st0le 好的,如果你给我一个你看到的 2p 解决方案的伪代码(将它粘贴到网络上的某个地方并留下链接),我可以轻松地将它变成 3p 解决方案。他们真的是一样的。
              • @Marcello Pastie 似乎已关闭,但 archive.org 在这里拯救了这一天:web.archive.org/web/20151031213948/pastie.org/1762895#8,12(也就是说,代码至少有两个错误,只是不起作用,我'很抱歉)。
              • 所以如果有 k 个分区,你应该创建一个维度为 (n * sum(w)/k * sum(w)/k * sum(w)/k * sum( w)/k .... k-1 次)。我在这里看到了用于解决相同问题的不同直觉-github.com/anoubhav/Coursera-Algorithmic-Toolbox/blob/master/…,他只是将其用作大小为 (n * sum(w)/k) 的二维数组
              猜你喜欢
              • 2019-03-19
              • 2021-12-21
              • 2020-03-14
              • 1970-01-01
              • 1970-01-01
              • 2015-10-18
              • 1970-01-01
              • 1970-01-01
              • 2017-03-30
              相关资源
              最近更新 更多