0-1背包問題-DP


中文理解:

0-1背包問題:有一個賊在偷竊一家商店時,發現有n件物品,第i件物品價值vi元,重wi磅,此處vi與wi都是整數。他希望帶走的東西越值錢越好,但他的背包中至多只能裝下W磅的東西,W為一整數。應該帶走哪幾樣東西?這個問題之所以稱為0-1背包,是因為每件物品或被帶走;或被留下;小偷不能只帶走某個物品的一部分或帶走同一物品兩次。

  在分數(部分)背包問題(fractional knapsack problem)中,場景與上面問題一樣,但是竊賊可以帶走物品的一部分,而不必做出0-1的二分選擇。可以把0-1背包問題的一件物品想象成一個金錠,而部分問題中的一件物品則更像金沙。

  兩種背包問題都具有最優子結構性質。對0-1背包問題,考慮重量不超過W而價值最高的裝包方案。如果我們將商品j從此方案中刪除,則剩余商品必須是重量不超過W-wj的價值最高的方案(小偷只能從不包括商品j的n-1個商品中選擇拿走哪些)。

  雖然兩個問題相似,但我們用貪心策略可以求解背包問題,而不能求解0-1背包問題,為了求解部分數背包問題,我們首先計算每個商品的每磅價值vi/wi。遵循貪心策略,小偷首先盡量多地拿走每磅價值最高的商品,如果該商品已全部拿走而背包未裝滿,他繼續盡量多地拿走每磅價值第二高的商品,依次類推,直到達到重量上限W。因此,通過將商品按每磅價值排序,貪心算法的時間運行時間是O(nlgn)。

  為了說明貪心這一貪心策略對0-1背包問題無效,考慮下圖所示的問題實例。此例包含3個商品和一個能容納50磅重量的背包。商品1重10磅,價值60美元。商品2重20磅,價值100美元。商品3重30磅,價值120美元。因此,商品1的每磅價值為6美元,高於商品2的每磅價值5美元和商品3的每磅價值4美元。因此,上述貪心策略會首先拿走商品1。但是,最優解應該是商品2和商品3,而留下商品1。拿走商品1的兩種方案都是次優的。

  但是,對於分數背包問題,上述貪心策略首先拿走商品1,是可以生成最優解的。拿走商品1的策略對0-1背包問題無效是因為小偷無法裝滿背包,空閑空間降低了方案的有效每磅價值。在0-1背包問題中,當我們考慮是否將一個商品裝入背包時,必須比較包含此商品的子問題的解與不包含它的子問題的解,然后才能做出選擇。這會導致大量的重疊子問題——動態規划的標識。

 

Knapsack Problem

 

The knapsack problem or rucksack problem is a problem in combinatorial optimization: Given a set of items, each with a weight and a value, determine the number of each item to include in a collection so that the total weight is less than or equal to a given limit and the total value is as large as possible.

It derives its name from the problem faced by someone who is constrained by a fixed-size knapsack and must fill it with the most valuable items.

Example of a one-dimensional (constraint) knapsack problem: which boxes should be chosen to maximize the amount of money while still keeping the overall weight under or equal to 15 kg?

knapsack problem

思路:

1.構建二維矩陣

2.找到最合適的項

3.程序表示

代碼如下:

// Solve 0/1 knapsack problem
  // Dynamic Programming approach.
  solveZeroOneKnapsackProblem() {
    // We do two sorts because in case of equal weights but different values
    // we need to take the most valuable items first.
    this.sortPossibleItemsByValue();
    this.sortPossibleItemsByWeight();

    this.selectedItems = [];

    // Create knapsack values matrix.
    const numberOfRows = this.possibleItems.length;
    const numberOfColumns = this.weightLimit;
    const knapsackMatrix = Array(numberOfRows).fill(null).map(() => {
      return Array(numberOfColumns + 1).fill(null);
    });

    // 初始化矩陣第一列
    for (let itemIndex = 0; itemIndex < this.possibleItems.length; itemIndex += 1) {
      knapsackMatrix[itemIndex][0] = 0;
    }

    //初始化矩陣第一行
    for (let weightIndex = 1; weightIndex <= this.weightLimit; weightIndex += 1) {
      const itemIndex = 0;
      const itemWeight = this.possibleItems[itemIndex].weight;
      const itemValue = this.possibleItems[itemIndex].value;
      knapsackMatrix[itemIndex][weightIndex] = itemWeight <= weightIndex ? itemValue : 0;
    }

    // Go through combinations of how we may add items to knapsack and
    // define what weight/value we would receive using Dynamic Programming
    // approach.
    for (let itemIndex = 1; itemIndex < this.possibleItems.length; itemIndex += 1) {
      for (let weightIndex = 1; weightIndex <= this.weightLimit; weightIndex += 1) {
        const currentItemWeight = this.possibleItems[itemIndex].weight;
        const currentItemValue = this.possibleItems[itemIndex].value;

        if (currentItemWeight > weightIndex) {
          knapsackMatrix[itemIndex][weightIndex] = knapsackMatrix[itemIndex - 1][weightIndex];
        } else {
          // 考慮是否選擇當前物品的重量與價值
          knapsackMatrix[itemIndex][weightIndex] = Math.max(
            currentItemValue + knapsackMatrix[itemIndex - 1][weightIndex - currentItemWeight],
            knapsackMatrix[itemIndex - 1][weightIndex],
          );
        }
      }
    }

    //對二維矩陣進行回溯,以確定相應的項------回溯這里的代碼看不大懂
    let itemIndex = this.possibleItems.length - 1;
    let weightIndex = this.weightLimit;

    while (itemIndex > 0) {
      const currentItem = this.possibleItems[itemIndex];
      const prevItem = this.possibleItems[itemIndex - 1];

      // Check if matrix value came from top (from previous item).
      // In this case this would mean that we need to include previous item
      // to the list of selected items.
      if (
        knapsackMatrix[itemIndex][weightIndex]
        && knapsackMatrix[itemIndex][weightIndex] === knapsackMatrix[itemIndex - 1][weightIndex]
      ) {
        // Check if there are several items with the same weight but with the different values.
        // We need to add highest item in the matrix that is possible to get the highest value.
        const prevSumValue = knapsackMatrix[itemIndex - 1][weightIndex];
        const prevPrevSumValue = knapsackMatrix[itemIndex - 2][weightIndex];
        if (
          !prevSumValue
          || (prevSumValue && prevPrevSumValue !== prevSumValue)
        ) {
          this.selectedItems.push(prevItem);
        }
      } else if (knapsackMatrix[itemIndex - 1][weightIndex - currentItem.weight]) {
        this.selectedItems.push(prevItem);
        weightIndex -= currentItem.weight;
      }

      itemIndex -= 1;
    }
  }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM