货箱装载、背包问题

220 阅读1分钟

货箱装载

n 个货箱, 重量分别为 w1 w2 w3 ... 货船能载重 c 问如何装箱, 才能装下更多的货箱

贪婪算法

每次选出 重量最小 的货箱, 这样就能够保证装载数量最大

function containerLoading(container, capacity) {
    // 按照重量进行升序
    let c = container.map((e, i) => {
        return {
            weight: e,
            id: i,
        };
    });
    c.sort((a, b) => a.weight - b.weight);
    
    let res = new Array(container.length).fill(0);
    // 每次选出重量最小的货箱, 直到超重
    for (let i = 0; i < c.length && c[i].weight <= capacity; i++) {
        res[c[i].id] = 1;
        capacity -= c[i].weight;
    }
    return res;
}

// 货箱重量
let container = [100, 200, 50, 90, 150, 50, 20, 80];

// 装载结果
let res = containerLoading(container, 400);
console.log(res);

0/1背包问题

n 个物品和一个容量为 c 的背包, 每个物品重量为 w(i) ,价值 p(i) , 如何装包才能使得物品重量不超过背包容量且装入的物品价值最大(每个物品至多选 1 次)

贪婪算法

function backpack(container, capacity) {
    let c = container.map((e, i) => {
        return {
            ...e,
            id: i,
            per: e.profit / e.weight,
        };
    });
    // 按照单位质量价值降序
    c.sort((a, b) => b.profit / b.weight - a.profit / a.weight);

    let res = new Array(container.length).fill(0);

    // 优先选取单价大的, 直到装满
    let profit = 0;
    for (let i = 0; i < c.length; i++) {
        if (c[i].weight <= capacity) {
            res[c[i].id] = 1;
            capacity -= c[i].weight;
            profit += c[i].profit;
        }
    }
    return {
        profit,
        res,
    };
}

// 背包重量和价值
let container = [
    {
        weight: 10,
        profit: 40,
    },
    {
        weight: 30,
        profit: 40,
    },
    {
        weight: 25,
        profit: 30,
    },
    {
        weight: 50,
        profit: 50,
    },
    {
        weight: 40,
        profit: 35,
    },
    {
        weight: 60,
        profit: 30,
    },
    {
        weight: 35,
        profit: 10,
    },
];

// 装载结果
let res = backpack(container, 150);
console.log(res);

递归算法

function backpack(container, capacity) {
    /**
     * @param {*} i 剩余物品 i, i+1, ..., n
     * @param {*} capacity 剩余容量
     * @param {*} container 物品
     * @returns 当前背包价值
     */
    function pack(i, capacity, container) {
        if (i == container.length - 1) {
            // 最后一个物品, 如果超了就不放, 不超就放
           return capacity < container[i].weight ? 0 : container[i].profit;
        } else if (capacity < container[i].weight) {
            // 不是最后一个商品, 物品重量超过容量, 直接丢弃, 继续看下一个物品
            return pack(i + 1, capacity, container);
        } else {
            // 当前物品还在容量范围内
            // 放第 i 件商品的价值
            let p1 = pack(i + 1, capacity - container[i].weight, container) + container[i].profit;
            // 不放第 i 件商品的价值
            let p2 = pack(i + 1, capacity, container);
            return Math.max(p1, p2);
        }
    }

    return pack(0, capacity, container);
}

相关问题:

494. 目标和

// sum(+) - sum(-) = target 
// => sum(nums) + sum(+) - sum(-) = sum(nums) + target
// => 2 × sum(+) = sum(nums) + target 
// => sum(+) = (sum(nums) + target) / 2
// 变成容量为 sum(+) 的背包问题
var findTargetSumWays = function (nums, target) {
    // 计算 nums 和
    let sum = nums.reduce((pre, cur) => pre + cur, 0);
    
    if (sum < Math.abs(target) || (sum + target) % 2 == 1) return 0;

    let capacity = (sum + target) / 2;

    let dp = new Array(capacity + 1).fill(0);
    dp[0] = 1; // sum(+)=0 时的解法数

    nums.forEach(num => { // 遍历物品
        // 从后往前遍历, 一直遍历到 num 
        // 也就是 sum(+)=j 的解法数等于 sum(+)=j-num 的解法数之和
        // 因为 sum(j-num) 的全部解法, 最后加一个 num 刚好等于 j
        // 比如 sum(+)=4 的解法数可以等于 sum(+)=5-1, sum(+)=6-2 等等的解法数之和
        for (let j = capacity; j >= num; j--) {
            dp[j] += dp[j - num];
        }
    });
    return dp[capacity];
};

console.log(findTargetSumWays([100], -200));