Algorithm 如何以最少的包装数量包装物品(不同重量)?

Algorithm 如何以最少的包装数量包装物品(不同重量)?,algorithm,combinatorics,Algorithm,Combinatorics,因此,我有重量为:w[1]、w[2]、…w[N]的N物品,还有几个袋子,每个袋子可以装M的重量 问题是:如何对项目进行分组,以便我可以用最少的包数来容纳所有项目 测试用例: 权重为[40,6,4,42,8,43,4,3,19,30]的项目 袋子可容纳M=50 一个可接受的答案:(40,6,4)(42,8)(43,4,3)(19,30)您的问题是线性箱包装的特殊情况,其中重量均为非负。在这种特殊情况下,贪婪第一拟合算法总是产生最优解 这些课堂笔记上给出的“证明”不正确: 正如@Fede所指出的,

因此,我有重量为:w[1]、w[2]、…w[N]N物品,还有几个袋子,每个袋子可以装M的重量

问题是:如何对项目进行分组,以便我可以用最少的包数来容纳所有项目

测试用例:

权重为[40,6,4,42,8,43,4,3,19,30]的项目

袋子可容纳M=50


一个可接受的答案:(40,6,4)(42,8)(43,4,3)(19,30)

您的问题是线性箱包装的特殊情况,其中重量均为非负。在这种特殊情况下,贪婪第一拟合算法总是产生最优解

这些课堂笔记上给出的“证明”不正确:

正如@Fede所指出的,如果我们重新安排示例的输入并应用第一次拟合算法,我们会得到一个非最优结果:

w[i]=(43,6,4,42,8,40,4,3,19,30),M=50

FF产量(43,6),(4,42),(8,40),(4,3,19),(30),即5袋


我只想删除这个答案,以避免没有彻底检查的羞耻感,但我希望这能防止其他人落入同样的陷阱。

第一个合适的贪婪并不能保证最佳包装。对于提供的样品,first fit可以尝试将重量为43的物品与重量为6的物品包装在一起,然后产生的包装将不少于5个,而不是4个

动态规划可能是一个非常有效的解决方案,它可以利用为较小的项目集解决问题的优势。我将在这里提出一个简单的回溯,如果性能不可接受,您可以在以后进行优化

我对此的看法如下。您可以编写一个简单的递归,按照以下规则为每个项分配包id:

  • 第一项将始终位于包0中
  • 任何其他项目只能位于已启动的包中,或启动新包
  • 包装中物品的重量总和不得超过M
这样的东西可以实现这套规则

int[] FindBestPackageForItems(int[] weights, int N, int M) {
    // in the worst scenario, you'll have N packages
    int lowestNumberOfBags = N;
    int[] bestPacking = new int[N];
    for (int i = 0; i < N; i++)
        bestPacking[i] = i;
    int[] weightInEachBag = new int[N];
    int[] currentPacking = new int[N];
    currentPacking[0] = 0; // First item is always in package 0
    weightInEachBag[0] = weights[0];
    int currentItem = 1; // Item 0 is already in bag 0, the next item to pack will be the item 1
    int usedBags = 1; // The first bag is already used by the first item
    PutItemsInBags(weights, N, M, currentPacking, weightInEachBag, currentItem, usedBags, bestPacking, ref lowestNumberOfBags);
    return bestPacking;
}

void PutItemsInBags(int[] weights, int N, int M, int[] currentPacking, int[] weightInEachBag, int currentItem, int usedBags, int[] bestPacking, ref int lowestNumberOfBags) {
    if (currentItem == N) {
        assert(usedBags < lowestNumberOfBags);
        for (int i = 0; i < N; i++)
            bestPacking[i] = currentPacking[i];
        lowestNumberOfBags = usedBags;
    }
    else {
        for (int p = 0; p < min(usedBags + 1, lowestNumberOfBags); p++) {
            if (weightInEachBag[p] + weights[currentItem] <= M) {
                weightInEachBag[p] += weights[currentItem];
                currentPacking[currentItem] = p;
                PutItemsInBags(weights, N, M, currentPacking, weightInEachBag, currentItem+1, max(p, usedBags), bestPacking, ref lowestNumberOfBags);
                weightInEachBag[p] -= weights[currentItem];
            }
        }
    }
}
int[]FindBestPackageForItems(int[]weights,int N,int M){
//在最坏的情况下,您将有N个包
int最低行李数=N;
int[]最佳打包=新int[N];
对于(int i=0;i如果(weightInEachBag[p]+weights[currentItem],我相信这是动态规划的经典任务使用动态规划。@EliKorvigo是的,但这似乎更高级,因为有多个袋子可能会排除贪婪的解决方案,对吗?@EliKorvigo谢谢你的回答。但我的问题与“用最少数量的硬币获得一笔钱”略有不同。正如Simon所评论的。此外,在我的例子中,硬币的总和并不总是等于给定的总和。例如,在我的测试用例中:(19+30)=49<50.我相信你的问题被称为@Simon是的,你的问题是一般问题的一个特例。对于这些特例,贪婪的方法是有效的。对于一般情况,它不是。但是你如何确定你是在简单情况下还是在一般情况下?看看:。或者你可以只实现一般的解决方案,也可以解决这个问题在编程挑战中,你通常会遇到一般问题……是的,这是背包问题的一个改进-