【问题标题】:Copying books using dynamic programming使用动态编程复制书籍
【发布时间】:2015-05-07 19:32:23
【问题描述】:

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

问题陈述:

在书籍印刷发明之前,复印非常困难 的一本书。所有内容都必须由所谓的手写重写 抄写员。抄写员得到了一本书,几个月后 他完成了它的副本。最著名的抄写员之一住在 15世纪,他的名字是Xaverius Endricus Remius Ontius Xendrianus(施乐)。总之,工作很烦很无聊。和 加快速度的唯一方法是雇佣更多的抄写员。

从前,有一个剧团想演奏 著名的古董悲剧。这些剧本的剧本分为 当然,许多书籍和演员需要更多的副本。所以他们 雇了许多抄写员来复印这些书。想象一下你有 m 可能有不同页数的书籍(编号为 1、2、....、m) ( p_1, p_2, ..., p_m) 并且您想为它们中的每一个制作一份副本。 你的任务是将这些书分给 k 个抄写员,k

我能够得到迭代描述的问题的最优解,但无法用它来找到问题所需的解,即:

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();
    }

这里有什么建议吗?是对解决方案的解释还是我在代码中翻译重复出现的方式有问题?

【问题讨论】:

  • 你的问题到底是什么,如果你在开头包含问题陈述会非常有帮助
  • @sasha 问题在问题附带的超链接中提供。
  • 是的,读了就知道你能说出到底是什么问题吗?是在编码/寻找测试用例的解释/提出 dp 解决方案。此外,通常问题应该是自包含的,没有链接,链接应该供进一步参考或详细解释
  • @faizanjehangir 超链接往往会死掉。请在问题本身中发布对问题陈述的简短说明。
  • @amit 我添加了有关问题陈述和问题本身的更多详细信息

标签: java algorithm dynamic-programming recurrence


【解决方案1】:

不确定您的解决方案,但这里有一种直观的带有记忆的递归方法。假设有 n 本书,第 i 本书有 pages[i] 页。也让有 m 个订阅者。如果我们只得到书籍 i,i+1.....n 并且只有 dp[i][j] strong>j 个订阅者来完成这项工作。以下是带有记忆的递归伪代码

    //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 上代码的运行版本。它不仅可以找到最佳答案,还可以打印出最佳分配(我没有包含在上面的伪代码中)。 (点击右上角的叉子,它会运行 您的测试用例,输入格式与您的相同)。输出将是最佳答案,然后是最佳分配。如果您对代码有疑问,请发表评论。

【讨论】:

  • 你能解释一下sub在这个过程中的使用吗?
  • sub 表示订阅人数
  • position 意味着只考虑从 position,position+1...n 开始的书。请参阅我的 dp[][] 数组的定义,它解释了位置( i )和子( j )。
  • @faizanjehangir 查看我的编辑并转到代码工作版本的链接
  • 索引有点混乱,但解决方案很完美!
猜你喜欢
  • 2019-05-22
  • 2021-04-30
  • 2017-04-17
  • 1970-01-01
  • 2014-10-15
  • 1970-01-01
  • 2011-09-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多