使用动态编程复制书籍

时间:2015-03-06 08:16:59

标签: java algorithm dynamic-programming recurrence

我正在实施用于复制图书问题的动态编程解决方案。解决方案的想法来自herehere

问题陈述:

  

在书籍印刷发明之前,制作副本非常困难   一本书。所有内容都必须由所谓的手工重写   划刻。几个月后,抄写员被给了一本书   他完成了副本。其中一位最着名的抄写员住在这里   15世纪,他的名字是Xaverius Endricus Remius Ontius   Xendrianus(Xerox)。无论如何,这项工作非常烦人和无聊。和   加快速度的唯一方法就是雇用更多的抄写员。

     

曾几何时,有一个想要演奏的剧院合奏   着名的古董悲剧。这些戏剧的剧本分为   当然,许多书籍和演员需要更多的副本。所以他们   雇了很多抄写员来复制这些书。想象一下,你有m   可能具有不同页数的书籍(编号为1,2,......,m)   (p_1,p_2,...,p_m)并且您想要为它们制作一份副本。   你的任务是将这些书分为k文士,k <= m。每本书   可以只分配给一个划线器,每个划线器必须得到一个   连续的书籍序列。这意味着,存在增加   数字序列0 = b_0&lt; b_1&lt; b_2,...&lt; b_ {k-1}&lt; = b_k = m $   这样,我的抄写员得到一系列数字之间的书   bi-1 + 1和bi。复制所有书籍所需的时间是   由被分配最多工作的抄写员决定。因此,   我们的目标是最小化分配给a的最大页数   单个划线员。您的任务是找到最佳分配。

我能够迭代地获得所描述问题的最佳解决方案,但无法使用它来找到问题所需的解决方案,即:

Sample input:
2
9 3
100 200 300 400 500 600 700 800 900
5 4
100 100 100 100 100

Sample Output
100 200 300 400 500 / 600 700 / 800 900
100 / 100 / 100 / 100 100

其中2是数据集的数量,9是书籍数量,3是分配书籍的抄写员数量。

以下是我输出的相应输入:

100 100 100 
300 300 300 
600 600 600 
1000 700 700 
1500 900 900 
2100 1100 1100 
2800 1300 1300 
3600 1500 1500 
4500 1700 1700 

100 100 100 100 
200 200 200 200 
300 300 300 300 
400 300 300 300 
500 300 300 300 

对于第一个解决方案集,我可以使用1700作为每个用户的最佳页面分配数量,并继续分配书页直到,当前划线页面总和> = 1700.但是,第二个解决方案没有任何它的模式是什么?

以下是我生成解决方案的代码:

private void processScribes(){
        int[][] bookScribe = new int[numOfBooks][numOfScribes];
        //set first row to b1 page number
        for (int j = 0; j < numOfScribes; ++j)
            bookScribe[0][j] = bookPages[0];

        //set first column to sum of book page numbers
        for (int row = 1; row < numOfBooks; ++row)
            bookScribe[row][0] = bookScribe[row - 1][0] + bookPages[row]; 

        //calculate the kth scribe using dp
        for (int i = 1; i < numOfBooks; ++i){
            for (int j = 1; j < numOfScribes; ++j){
                //calculate minimum of maximum page numbers
                //from k = l + 1 to i
                //calculate sum 
                int minValue = 1000000;
                for (int k = 0; k < i - 1; ++k){
                    int prevValue = bookScribe[i - k][j - 1];
                    int max = 0;
                    int sumOflVals = 0;
                    for (int l = k + 1; l <= i; ++l){
                        sumOflVals = sumOflVals + bookPages[l];
                    }
                    if (prevValue > sumOflVals){
                        max = prevValue;
                    }
                    else
                        max = sumOflVals;
                    if (max < minValue )
                        minValue = max;
                }
                if (minValue == 1000000)
                    minValue = bookScribe[i][0];
                //store minvalue at [i][j]
                bookScribe[i][j] = minValue;
            }
        }

        //print bookScribes
        for (int i = 0; i < numOfBooks; ++i){
            for (int j = 0; j < numOfScribes; ++j)
                System.out.print(bookScribe[i][j] + " ");
            System.out.println();
        }
        System.out.println();
    }

这里有指针吗?它是解决方案的解释,或者我在代码中翻译重复的方式有问题吗?

1 个答案:

答案 0 :(得分:2)

不确定您的解决方案,但这是一个带记忆的直观递归方法。让 n 书籍与ith书籍页面[i] 页面。还有 m 订阅者。如果我们只获得了 i,i + 1 ..... n 的书籍,那么 dp [i] [j] 也是问题的答案,并且只有< strong> j 用户可以完成这项工作。以下是带有memoization的递归伪代码

    //dp[][] is memset to -1 from main
    // Assuming books are numbered 1 to n
    // change value of MAX based on your constraints
    int MAX = 1000000000;
    int rec(int position , int sub )
    {
          // These two are the base cases
          if(position > n)
          {
             if(sub == 0)return 0;
             return MAX;
          }
          if(sub == 0)
          {
              if(position > n)return 0;
              return MAX;
          }

          // If answer is already computed for this state return it
          if(dp[position][sub] != -1)return dp[position][sub];

          int ans = MAX,i,sum = 0;
          for(i = position; i <= n;i++)
          {
             sum += pages[i];
             // taking the best of all possible solutions
             ans = min(ans,max(sum,rec(i+1,sub-1)));
          }
          dp[position][sub]=ans;
          return ans; 
    }

    //from main call rec(1,m) which is your answer

您可以通过动态编程将其转换为迭代解决方案,它将在时间和空间上具有相同的复杂性。空间 O(nm)且时间 O(n ^ 2.m) )


编辑
这里看一下测试用例Book Copying Code 上运行的代码版本。它不仅找到最佳答案,而且还打印出最佳分配(我没有包含在上面的伪代码中)。 (点击右上角的叉子,它会继续运行 你的测试用例,输入格式与你的相同)。输出将是最佳答案,然后是最佳分配。如果您对代码有疑问,请发表评论。