3-分区问题

发布于 2024-10-14 15:48:02 字数 478 浏览 4 评论 0原文

这是另一个动态规划问题(Vazirani ch6

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

总和(I) = 总和(J) = 总和(K) = 1/3*总和(全部)

例如,对于输入 (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分区,但还是无法解决

here is another dynamic programming question (Vazirani ch6)

Consider the following 3-PARTITION
problem. Given integers a1...an, we
want to determine whether it is
possible to partition of {1...n} into
three disjoint subsets I, J, K such
that

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

For example, for input (1; 2; 3; 4; 4;
5; 8) the answer is yes, because there
is the partition (1; 8), (4; 5), (2;
3; 4). On the other hand, for input
(2; 2; 3; 5) the answer is no. Devise
and analyze a dynamic programming
algorithm for 3-PARTITION that runs in
time poly- nomial in n and (Sum a_i)

How can I solve this problem? I know 2-partition but still can't solve it

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

救星 2024-10-21 15:48:02

很容易将 2 组解决方案推广到 3 组情况。

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

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

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

如果原始复杂度为 O(TOTAL*n),则此处为 O(TOTAL^2*n)
从最严格的意义上来说,它可能不是多项式,但原始版本也不是严格的多项式:)

It's easy to generalize 2-sets solution for 3-sets case.

In original version, you create array of boolean sums where sums[i] tells whether sum i can be reached with numbers from the set, or not. Then, once array is created, you just see if sums[TOTAL/2] is true or not.

Since you said you know old version already, I'll describe only difference between them.

In 3-partition case, you keep array of boolean sums, where sums[i][j] tells whether first set can have sum i and second - sum j. Then, once array is created, you just see if sums[TOTAL/3][TOTAL/3] is true or not.

If original complexity is O(TOTAL*n), here it's O(TOTAL^2*n).
It may not be polynomial in the strictest sense of the word, but then original version isn't strictly polynomial too :)

对岸观火 2024-10-21 15:48:02

我认为通过归约,它是这样的:

将2分区减少到3分区:

设S为原始集合,A为其总和,然后令S'=union({A/2},S)。
因此,对集合 S' 执行 3 分区会产生三个集合 X、Y、Z。
X、Y、Z中,其中之一必须是{A/2},假设它是集合Z,则X和Y是2分区。
S' 上 3 分区的见证人就是 S 上 2 分区的见证人,因此 2 分区减少为 3 分区。

I think by reduction it goes like this:

Reducing 2-partition to 3-partition:

Let S be the original set, and A be its total sum, then let S'=union({A/2},S).
Hence, perform a 3-partition on the set S' yields three sets X, Y, Z.
Among X, Y, Z, one of them must be {A/2}, say it's set Z, then X and Y is a 2-partition.
The witnesses of 3-partition on S' is the witnesses of 2-partition on S, thus 2-partition reduces to 3-partition.

夜空下最亮的亮点 2024-10-21 15:48:02

如果这个问题是可以解决的;那么 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) 和下一个状态 R' 中的下一个数字 n,它保持 R '(i+n,j,k)R'(i,j+n,k)R'(i,j,k+n)代码>.请注意,复杂性(根据练习大小)取决于输入数字的大小;这是一个伪多项式时间算法。 Nikita 的解决方案在概念上类似,但比该解决方案更有效,因为它不跟踪第三组的总和:这是不必要的,因为您可以轻松计算它。

If this problem is to be solvable; then sum(ALL)/3 must be an integer. Any solution must have SUM(J) + SUM(K) = SUM(I) + sum(ALL)/3. This represents a solution to the 2-partition problem over concat(ALL, {sum(ALL)/3}).

You say you have a 2-partition implementation: use it to solve that problem. Then (at least) one of the two partitions will contain the number sum(ALL)/3 - remove the number from that partion, and you've found I. For the other partition, run 2-partition again, to split J from K; after all, J and K must be equal in sum themselves.

Edit: This solution is probably incorrect - the 2-partition of the concatenated set will have several solutions (at least one for each of I, J, K) - however, if there are other solutions, then the "other side" may not consist of the union of two of I, J, K, and may not be splittable at all. You'll need to actually think, I fear :-).

Try 2: Iterate over the multiset, maintaining the following map: R(i,j,k) :: Boolean which represents the fact whether up to the current iteration the numbers permit division into three multisets that have sums i, j, k. I.e., for any R(i,j,k) and next number n in the next state R' it holds that R'(i+n,j,k) and R'(i,j+n,k) and R'(i,j,k+n). Note that the complexity (as per the excersize) depends on the magnitude of the input numbers; this is a pseudo-polynomialtime algorithm. Nikita's solution is conceptually similar but more efficient than this solution since it doesn't track the third set's sum: that's unnecessary since you can trivially compute it.

余生一个溪 2024-10-21 15:48:02

正如我在另一个类似问题中回答过的那样,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];
}

As I have answered in same another question like this, the C++ implementation would look something like this:

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];
}
涙—继续流 2024-10-21 15:48:02

假设您想要将 $X = {x_1, ..., x_n}$ 集合划分为 $k$ 分区。
创建一个$n\timesk$表。假设成本 $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 )

Let's say you want to partition the set $X = {x_1, ..., x_n}$ in $k$ partitions.
Create a $ n \times k $ table. Assume the cost $M[i,j]$ be the maximum sum of $i$ elements in $j$ partitions. Just recursively use the following optimality criterion to fill it:

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 )
智商已欠费 2024-10-21 15:48:02

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

检查逻辑:排除A[seq]元素到第三个和(不存储),检查没有元素的单元格是否有相同的两个和; OR 包含到 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;
}

Create a three dimensional array, where size is count of elements, and part is equal to to sum of all elements divided by 3. So each cell of array[seq][sum1][sum2] tells can you create sum1 and sum2 using max seq elements from given array A[] or not. So compute all values of array, result will be in cell array[using all elements][sum of all element / 3][sum of all elements / 3], if you can create two sets without crossing equal to sum/3, there will be third set.

Logic of checking: exlude A[seq] element to third sum(not stored), check cell without element if it has same two sums; OR include to sum1 - if it is possible to get two sets without seq element, where sum1 is smaller by value of element seq A[seq], and sum2 isn't changed; OR include to sum2 check like previous.

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;
}
还在原地等你 2024-10-21 15:48:02

C++ 中的另一个示例(基于之前的答案):

bool partition3(vector<int> const &A) {
  int sum = 0;
  for (int i = 0; i < A.size(); i++) {
    sum += A[i];
  }

  if (sum % 3 != 0) {
    return false;
  }

  vector<vector<vector<int>>> E(A.size() + 1, vector<vector<int>>(sum / 3 + 1, vector<int>(sum / 3 + 1, 0)));

  for (int i = 1; i <= A.size(); i++) {
    for (int j = 0; j <= sum / 3; j++) {
      for (int k = 0; k <= sum / 3; k++) {
        E[i][j][k] = E[i - 1][j][k];
        if (A[i - 1] <= k) {
          E[i][j][k] = max(E[i][j][k], E[i - 1][j][k - A[i - 1]] + A[i - 1]);
        }

        if (A[i - 1] <= j) {
          E[i][j][k] = max(E[i][j][k], E[i - 1][j - A[i - 1]][k] + A[i - 1]);
        }
      }
    }
  }
  
  return (E.back().back().back() / 2 == sum / 3);
}

Another example in C++ (based on the previous answers):

bool partition3(vector<int> const &A) {
  int sum = 0;
  for (int i = 0; i < A.size(); i++) {
    sum += A[i];
  }

  if (sum % 3 != 0) {
    return false;
  }

  vector<vector<vector<int>>> E(A.size() + 1, vector<vector<int>>(sum / 3 + 1, vector<int>(sum / 3 + 1, 0)));

  for (int i = 1; i <= A.size(); i++) {
    for (int j = 0; j <= sum / 3; j++) {
      for (int k = 0; k <= sum / 3; k++) {
        E[i][j][k] = E[i - 1][j][k];
        if (A[i - 1] <= k) {
          E[i][j][k] = max(E[i][j][k], E[i - 1][j][k - A[i - 1]] + A[i - 1]);
        }

        if (A[i - 1] <= j) {
          E[i][j][k] = max(E[i][j][k], E[i - 1][j - A[i - 1]][k] + A[i - 1]);
        }
      }
    }
  }
  
  return (E.back().back().back() / 2 == sum / 3);
}
北方。的韩爷 2024-10-21 15:48:02

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

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

You really want Korf's Complete Karmarkar-Karp algorithm (http://ac.els-cdn.com/S0004370298000861/1-s2.0-S0004370298000861-main.pdf, http://ijcai.org/papers09/Papers/IJCAI09-096.pdf). A generalization to three-partitioning is given. The algorithm is surprisingly fast given the complexity of the problem, but requires some implementation.

The essential idea of KK is to ensure that large blocks of similar size appear in different partitions. One groups pairs of blocks, which can then be treated as a smaller block of size equal to the difference in sizes that can be placed as normal: by doing this recursively, one ends up with small blocks that are easy to place. One then does a two-coloring of the block groups to ensure that the opposite placements are handled. The extension to 3-partition is a bit complicated. The Korf extension is to use depth-first search in KK order to find all possible solutions or to find a solution quickly.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文