【问题标题】:knapsack variation algorithm背包变异算法
【发布时间】:2012-04-30 15:57:07
【问题描述】:

作为作业,我有以下程序要在 java 中制作:

在一个书柜里,我们有一堆 N 本书,必须由 K 个作家手工抄写。 每本书都有 Ui 页面,其中 Ai 是书。

我们需要从堆栈中给每个作者连续的书,我们不能拆分书的页面。

制作一个程序,将书籍提供给作家,但通过最小化作家将复制的最大页数。

作为输入,用户将给出一串数字,其中第一个数字是书的数量 N,第二个数字是作者的数量 K,其余的数字是每本书的页数。

作为输出,程序将向用户输出作者将复制的最大页数。

例子:

输入:15 ​​6 30 40 10 40 50 20 30 40 10 70 10 50 30 50 10
输出:90

在此示例中,第一个作者可以取 book1 = 30 和 book2 = 40,但不能取例如 book1 = 30 和 book3 = 10。换句话说,您只能从堆栈顶部取书,而不会将它们混合在一起。

这是我的实现:

import java.util.*;

public class Library {

public static void main(String[] args) {
    Scanner input = new Scanner(System.in);

    // to work with 1.6 erase the second "Integer"
    //in 1.7 this works properly
    List<Integer> booksList = new LinkedList<Integer>();
    System.out.printf("Give: ");

    String answer = input.nextLine();
    String[] arr = answer.split(" ");

    for (String num : arr) {
        booksList.add(Integer.parseInt(num));
    }

    int books = booksList.remove(0);
    int writers = booksList.remove(0);

    while (booksList.size() > writers) {
        mergeMinimalPair(booksList);
    }

    System.out.println(getMax(booksList));
}

public static void mergeMinimalPair(List<Integer> books) {
    int index = 0;
    int minValue = books.get(0) + books.get(1);
    for (int i = 1; i < books.size() - 1; i++) {
        if ((books.get(i) + books.get(i + 1)) < minValue) {
            index = i;
            minValue = books.get(i) + books.get(i + 1);
        }
    }
    combine(books, index, index + 1);
}

public static void combine(List<Integer> books, int indexA, int indexB) {
    Integer a = books.get(indexA);
    Integer b = books.get(indexB);
    books.remove(indexB); 
    books.add(indexA, a + b);
    books.remove(indexB);
}

public static int getMax(List<Integer> books) {
    int max = books.get(0);
    for (int i = 1; i < books.size(); i++) {
        if (books.get(i) > max) {
            max = books.get(i);
        }
    }
    return max;
}
}

我所做的是每次我将最小的书对合并在一起,直到我的列表长度等于作者的数量,但它不起作用,在示例中它不是 90,而是输出 100。

我听说过动态编程解决方案和背包类问题的残酷解决方案,但在我的大学里,他们还没有教我们动态编程,所以教授要么对我们所知道的东西感到困惑,要么他希望我们找到一个残酷的解决方案。

我确信我的解决方案会起作用,但由于某种原因它不起作用,如果您能指出我在此解决方案中的其他解决方案或我弄错的地方的提示,我将非常高兴。

您可以将我指向 DP 解决方案或 Brutal 解决方案,但如果您将我指向 DP 解决方案,请注意我对 DP 实施几乎一无所知。

编辑:我已经查看了一些类似背包的问题,​​但我找不到具有这种变体和我能够理解的非 DP 解决方案的问题

【问题讨论】:

  • 我可以在这里看到很多解决方案stackoverflow.com/…>。
  • @g13n 我在这个网站上查看了一些类似背包的问题,​​但我找不到这个特殊的变化,尤其是没有 DP 解决方案
  • 你检查过你的相关问题吗,我可以看到一堆暴力解决方案;-)
  • input 是否完整?
  • 我知道这听起来很愚蠢,但大多数问题都有重量和价值的元素,我无法将这些问题与书籍和页面联系起来(可能是因为我的英语不好) 这就是为什么我无法理解如何针对我自己的问题实施这些解决方案

标签: java algorithm knapsack-problem


【解决方案1】:

你可以对答案进行二分搜索。为作者选择一个最大值,例如M,然后从左到右扫描书籍数组,为每个作者分配尽可能多的书籍,但不超过M。如果还有书,则必须增加M。如果您已成功分配所有书籍,请减少M

【讨论】:

  • 我之前已经将其视为一个有效的答案,只是我的编程技能不太擅长做一些微妙的事情
  • 你能不能给我一些关于如何实现这个的更多细节?
  • 选择一个M。没关系,从1开始。从书堆的顶部开始,开始将书籍分配给第一个作者。继续将书籍分配给第一个作者,一次一个,直到下一本书为第一个作者分配超过M 页。第一个作家完成了,从第二个作家重新开始。等等。如果您成功分配所有书籍,请M++ 再试一次。如果您还有剩余书籍,请M-- 再试一次。
【解决方案2】:

这被称为partition problem 的优化版本。它是 NP 难的。还有一个相当slick article 关于它。据我所知,有很多启发式方法可以逼近它,但没有明确设计用于“走捷径”同时获得准确答案的方法。

我之前遇到过类似的问题,我的实际实现最终成为一种启发式方法(贪婪方法适用于任意数量的分区很简单),然后进行了几次优化迭代(尝试交换/移动一些组之间的权重),在每次优化后进行检查,如果解决方案不可能更好(w 写入器的 p 页意味着每个写入器的 p/w 页是最佳的,尽管如果 w 不除 p恰好 p/w+1 是最优的)。在您的情况下,由于您正在寻找一个精确的解决方案,因此您最终需要一个暴力破解的备份案例。

请注意,您只是被询问其中一个分区的最大总和是多少。这实际上是 NP-hard 本身 - 知道更少的信息只不过是一个常数因子捷径。

如果我是你,我会蛮力的。对于少量书籍(少于 10 到 20 页)和大量页数(100 到 1000),接近 p/w 很可能无法达到提前退出的条件。另一方面,如果您需要处理任意数量的书籍,则对小尺寸使用蛮力,对较大尺寸使用近似值。

【讨论】:

  • 我实际上已经尝试过每个作家解决方案的页面,是的,如果它没有完全划分,那么它会导致无处
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-06
  • 2016-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多