分数背包

535 阅读2分钟

分数背包问题(Fractional Knapsack Problem)是一种经典的组合优化问题,它描述了如何从一组物品中选择部分物品装入背包,以使得装入背包的物品总价值最大。与 0/1 背包问题不同,分数背包问题允许将物品分割为更小的部分,以便更好地利用背包的容量。

问题描述如下:

给定一组物品,每个物品都有一个重量(weight)和一个价值(value)。同时给定一个背包的容量(capacity)。目标是选择一些物品,使得它们的总重量不超过背包的容量,且总价值最大。

分数背包问题的解决方法通常涉及贪心算法。以下是一种基本的贪心策略:

  1. 计算每个物品的单位价值:将每个物品的价值除以其重量,得到单位重量的价值。
  2. 按照单位价值排序:将物品按照单位价值降序排列,这样在贪心选择时可以优先选择单位价值最高的物品。
  3. 贪心选择:从单位价值最高的物品开始,依次将物品加入背包,直到背包装满。

这种贪心策略的正确性证明并不难,它基于这样的观察:在有限容量的背包中,优先选择单位价值最高的物品是最优解的一部分。如果采用其他策略,可能会导致浪费背包容量,从而得到的解不是最优的。

/**
 * 1.n个物品都是液体,有重量和价值
 * 2.现在你要取走 10升 的液体
 * 3。每次可以不拿,全拿,或拿一部分,问最高价值是多少
 * 
 * 编号 重量(升) 价值
 *  0    4      24      水
 *  1    8      160     牛奶
 *  2    2      4000    五粮液
 *  3    6      108     可乐
 *  4    1      4000    茅台
 */
public class FractionalKnapsackProblem {

    static class Item {

        int index;
        int weight;
        int value;

        public Item(int index, int weight, int value) {
            this.index = index;
            this.weight = weight;
            this.value = value;
        }

        public int unitValue() {
            return value / weight;
        }

        @Override
        public String toString() {
            return "Item(" +
                    "index=" + index +
                    ')';
        }
    }

    public static void main(String[] args) {

        Item[] items = new Item[]{
                new Item(0, 4, 24),
                new Item(1, 8, 160),
                new Item(2, 2, 4000),
                new Item(3, 6, 108),
                new Item(4, 1, 4000),
        };
        select(items, 10);

    }

    static void select(Item[] items, int total) {
        Arrays.sort(items, Comparator.comparingInt(Item::unitValue).reversed());
        int max = 0; //最大价值
        for (Item item : items) {
            System.out.println(item);
            if (total >= item.weight) {//可以拿完
                total -= item.weight;
                max += item.value;
            } else { //拿不完
                max += total * item.unitValue();
                break;
            }
        }
        System.out.println("最大价值是:"+max);
    }

}