坚持面试问题......数组的分区

时间:2011-06-23 13:14:19

标签: algorithm dynamic-programming

我在互联网上发现了以下问题,并想知道我将如何解决它:

  

问题:没有重新排列的整数分区

     

输入:非负数的排列S {s 1 ,. 。 。   ,s n }和一个整数k。

     

输出:将S分区为k个或更少的范围,以最大限度地减少最大值   所有k个或更少范围的总和,   没有重新排序任何   号。*

请帮助,看起来很有趣的问题......我实际上花了很多时间,但没有看到任何解决方案..

2 个答案:

答案 0 :(得分:21)

让我们尝试使用动态编程解决问题。

注意:如果 k> n 我们只能使用 n 区间。

S = { s 1 d [i] [j] 是问题的解决方案>,..., s i }和 k = j 。所以很容易看出:

  1. d [0] [j] = 0 每个 j 1 k
  2. d [i] [1] =每个 i的总和(s 1 ... s i 1 n
  3. d [i] [j] = min for t = 1 to i (max(d [i - t] [j - 1],sum(s i) - t + 1 ... s i ))i = 1到n,j = 2到k
  4. 现在让我们看看为什么会这样:

    1. 当序列中没有元素时,很明显只有一个区间(空元素)和元素之和为0.这就是为什么 d [0] [j] = 0 for j 1 k
    2. 当只有一个区间时,很明显解是所有序列元素的总和。所以 d [i] [1] = sum(s 1 ... s i
    3. 现在让我们考虑序列中有 i 元素,区间数是 j ,我们可以假设最后一个区间是(s i - t + 1 ... s i )其中 t 是不大于 i 的正整数,因此在这种情况下解决方案是 max(d [i - t] [j - 1],sum(s i - t + 1 ... s i ,但是as我们希望解决方案最小化我们应该选择 t 以使其最小化,因此我们将得到 min for t = 1 to i (max(d [i - t] [j - 1],sum(s i - t + 1 ... s i ))
    4. 示例:

      S =(5,4,1,12),k = 2

      d [0] [1] = 0,d [0] [2] = 0

      d [1] [1] = 5,d [1] [2] = 5

      d [2] [1] = 9,d [2] [2] = 5

      d [3] [1] = 10,d [3] [2] = 5

      d [4] [1] = 22,d [4] [2] = 12

      <强>代码:

      #include <algorithm>
      #include <vector>
      #include <iostream>
      using namespace std;
      
      int main ()
      {
          int n;
          const int INF = 2 * 1000 * 1000 * 1000;
          cin >> n;
          vector<int> s(n + 1);
          for(int i = 1; i <= n; ++i)
              cin >> s[i];
          vector<int> first_sum(n + 1, 0);
          for(int i = 1; i <= n; ++i)
              first_sum[i] = first_sum[i - 1] + s[i];
          int k;
          cin >> k;
          vector<vector<int> > d(n + 1);
          for(int i = 0; i <= n; ++i)
              d[i].resize(k + 1);
          //point 1
          for(int j = 0; j <= k; ++j)
              d[0][j] = 0;
          //point 2
          for(int i = 1; i <= n; ++i)
              d[i][1] = d[i - 1][1] + s[i]; //sum of integers from s[1] to s[i]
          //point 3
          for(int i = 1; i <= n; ++i)
              for(int j = 2; j <= k; ++j)
              {
                  d[i][j] = INF;
                  for(int t = 1; t <= i; ++t)
                      d[i][j] = min(d[i][j], max(d[i - t][j - 1], first_sum[i] - first_sum[i - t]));
              }
      
      
          cout << d[n][k] << endl;
          return 0;
      }
      

答案 1 :(得分:8)

这个问题是从Steven Skiena的书“The Algorithm Design Manual”中逐字记录下来的。您可以在Google Books上阅读详细讨论及其解决方案。更好的是,buy the book