【问题标题】:knapsack 01 with twist双肩背包 01
【发布时间】:2015-12-04 07:10:52
【问题描述】:

我正在用 Java 做一个背包,我们只使用权重没有价值。 weightlimit 是 1000。我们从使用的键盘扫描了 5 个权重。 扭曲的是,只要壁橱达到 1000,您实际上可以超过 1000。因此,在一种情况下,我们有 2 个可能的权重 990 和 1010,并且程序应该选择较高的一个。 扫描的数字永远不会高于 1000。

package kapsackidone;
import java.util.Scanner;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.*;
public class Kapsack {

public static void main(String[] args) throws Exception {
    BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));

    int [] wt=new int[5];
    int W = 1000;

    System.out.println("Enter Weight 5 weights");
    for(int i=0; i<5; i++)
    {
        wt[i]=Integer.parseInt(reader.readLine());
    }   
    System.out.println(knapsack(wt, W));
}

public static int knapsack(int wt[], int W) {
    int N = wt.length;
    int[][] V = new int[N + 1][W + 1];

    for (int col = 0; col <= W; col++) {
        V[0][col] = 0;
    }


    for (int row = 0; row <= N; row++) {
        V[row][0] = 0;
    }

    for (int item=1;item<=N;item++){

    for (int weight=1;weight<=W;weight++){
        if(wt[item-1] > weight)
        {
            V[item][weight] = V[item-1][weight];
        }
        else if((weight - V[item-1][weight]) < (weight - (V[item-1][weight - wt[item-1]] + wt[item-1])))
        {
            V[item][weight] = V[item-1][weight];
        }
        else
        {
            V[item][weight] = V[item-1][weight - wt[item-1]] + wt[item-1];
        }
    }
    }

    return V[N][W];
    }
}

我真的在为如何完成这项工作而苦苦挣扎。 在你问不之前,这不是家庭作业,我将成为一个由开发人员组成的新团队的项目经理,所以我只是想学习一些 Java,以便我了解他们所做的一些事情,即使我怀疑我能否做到帮助编码。

【问题讨论】:

  • 问题是第二小的权重(不可行解的最小可能值)可以任意大。考虑两个项目i1=1i2=M,其中M 是一个非负整数;假设背包容量为1。唯一可行的解​​决方案是i1,权重为1,而最小的不可行解决方案是i2,其值为M。当M 趋于无穷大时,M-1(到最优值的附加距离)和M/1(最优值与最小可能违反容量的比率)都不是有界的。
  • 所以这是我在 kattis 网上找到的任务。我们想举起尽可能接近 1000 公斤。如果存在两个同样接近 1000 的数字(例如 998 和 1002),我们将选择较大的一个(在本例中为 1002)。输入 输入的第一行包含板数 n (1≤n≤1000)。以下 n 行中的每一行包含一个小于或等于 1000 的正整数,表示每个盘子的重量。输出 输出一个整数,组合权重最接近 1000。
  • 我忘了说重量是恒定的 1000 并且扫描的数字永远不会超过 1000 他们只能是 500 和 700 等于总和 1200 但一个单一的重量永远不会超过1000

标签: java knapsack-problem


【解决方案1】:

我只会运行两次。

在第一次运行中找到最佳权重小于 1000 的“经典”解决方案。

在第二次运行中,将最大值 1000 增加到基于先前解决方案允许的最大可能值。

不要担心“它会慢两倍”,将复杂度乘以常数不会改变复杂度,这是背包问题中的重要内容。


如果您的代码正常工作,那么您可能会认为这是最好的解决方案

System.out.println(knapsack(wt,2*W - knapsack(wt, W));

或者你可以这样写,以便更清楚发生了什么(它与上面的一行完全相同)

int bestClassicSolution = knapsack(wt, W);
int differenceAgainstMaxWeight = W - bestClassicSolution;
int newMaxWeight = W + differenceAgainstMaxWeight;
int bestSolution = knapsack(wt, newMaxWeight);
System.out.println(bestSolution);

编辑:上面的解决方案适用于这种情况select as big solution as possible, but it must not differ from 1000 more than "below 1000" best solution。 OP 实际上想要一点不同的东西 - “限制”保持不变,但它应该最接近 1000 但尽可能高。


因此真正的解决方案是创建反向背包方法,该方法将找到具有最小值但必须大于“min”变量的解决方案。

public static void main(String[] args) throws Exception {
    BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));

    int [] wt=new int[5];
    int W = 1000;

    System.out.println("Enter Weight 5 weights");
    for(int i=0; i<5; i++)
    {
        wt[i]=Integer.parseInt(reader.readLine());
    }   
    int bestClassicSolution = knapsack(wt, W);
    int differenceAgainstMaxWeight = W - bestClassicSolution;
    int newMaxWeight = W + differenceAgainstMaxWeight;
    int bestMaxSolution = reversedKnapsack(wt, newMaxWeight, W);
    int differenceAgainstWeightAboveW = W - bestMaxSolution;

    if (differenceAgainstWeightAboveW <= differenceAgainstMaxWeight){
        System.out.println(bestMaxSolution);
    } else {
        System.out.println(bestClassicSolution);
    }
}

public static int reversedKnapsack(int wt[], int W, int min) {
    //similar to knapsack method, but the solution must be as small as possible and must be bigger than min variable
}

【讨论】:

  • 嗯,你的第三段应该是什么意思?
  • 背包是著名的“NPC问题”,复杂性也很重要:)。有趣的是——只要不改变复杂性,程序是快两倍还是慢一倍并不重要。
  • 好吧,您可以在网站上将其上交并对其进行评分,检查运行时间等,并且确实能够做到这一点,我认为我需要它在 1 次运行中双向工作。
  • 它将是“一次运行”,只需添加运行两次的 for-cycle ...运行时间远远超过“运行一次”或“运行两次”。此外,运行时可能会检查复杂度不大于 (2^n)
  • 您能否在代码示例中向我展示它是如何完成的。正如我所说的,我不是一个只想学习的开发人员,并且算法确实可以很好地转化为代码。对不起,如果我打扰了..
【解决方案2】:

来自维基百科的逐字记录——Subset sum problem

可以使用动态规划在伪多项式时间内解决该问题。假设序列是

x1, ..., xN 我们希望确定是否存在总和为零的非空子集。将布尔值函数 Q(i, s) 定义为 if 的值(真或假)

“有一个 x1, ..., xi 的非空子集,总和为 s”。 因此,“给定一组整数,是否存在总和为零的非空子集?”问题的解决方案。是Q(N, 0)的值。

令 A 为负值之和,B 为正值之和。显然,如果 s B,Q(i, s) = false。所以这些值不需要存储或计算。

创建一个数组来保存 1 ≤ i ≤ N 和 A ≤ s ≤ B 的值 Q(i, s)。

现在可以使用简单的递归填充数组。最初,对于 A ≤ s ≤ B,设置

Q(1, s) := (x1 == s) 其中 == 是一个布尔函数,如果 x1 等于 s,则返回 true,否则返回 false。

然后,对于 i = 2,…,N,设置

Q(i, s) := Q(i − 1, s) or (xi == s) or Q(i − 1, s − xi),对于 A ≤ s ≤ B。

计算完Q的值后,我们可以循环遍历,取最接近极限的真值。

至于S的值,我们需要取给我们的权重之和。

【讨论】:

  • 不是我投反对票的人,但正如我上面所说,算法确实对我有帮助,因为我在编码方面有点新手,因此代码示例让我更容易理解,因为然后我可以搜索关键字并阅读代码而不是数学中的示例。
【解决方案3】:

经典背包问题在Wikipedia article 中讨论;经典问题的动态规划公式可以适应以下问题。

给定权重w_1,...,w_n 和目标容量W,找到总权重最小但大于W 的项目子集。

为了避免病态的情况,我们假设权重之和大于W,否则无解。让W_MAX 表示所有权重的总和。

对于动态规划公式,让

m[i,j] for each i in 0,...,n and j in 0,...,W_MAX

表示大于W 的最小权重,通过丢弃0,...,i 的权重,总权重恰好为j

我们获得

m[0,j] = W_MAX for each j in 0,...n

得到递归关系

m[i,j] = min {
               m[i-1, i       ],       // corresponds to keeping weight i
               m[i-1, j - w[i]] - w[i] // corresponds to discarding weight i
             }

并且可以通过迭代i=0,...,nj=0,...,W_MAX来实现评估;在这些界限之外加入m 必须假定产生W_MAX。类似于经典的背包问题,实际要丢弃的物品集可以通过backtracking找到。

最后,给定的实例可以优化两次;先用上面的算法,再用经典的背包算法。

【讨论】:

    【解决方案4】:

    我首先将这个问题作为一个经典的背包问题来评估
    value[i] = weight[i] ;
    ,其中i 是第 i 个物品,最大重量是给定的 max_wt (1000 kg) 和 item[ ] 是一个数组,其中包含按重量升序排列的项目。

    让这个问题的答案是 x,比如 990 公斤,现在我将计算差异 'd',
    d = max_wt - x ; 遍历 item[] 直到 item[i] 超过 d :
    int i = 0 ; while(item[i] < d ) i++; ,最后将超过'd'的第一项添加到您通过经典背包问题得到的答案中:
    answer = dp[n-1][w-1] + item[i] \\dp[n-1][w-1] is the answer of the classical \\knapsack problem

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-09
      • 2011-02-02
      • 2016-12-31
      相关资源
      最近更新 更多