动态规划算法之分组问题(背包问题变种)-其一

303 阅读2分钟

题目描述

给一个整数数组 array,把他分为两组 a 和 b,要使得 a 和 b 两组的元素之和的差最小,输出最小值(或者输出最小值的分组情况) 力扣上面第1049题也是该问题的一种变形问法1049. 最后一块石头的重量 II

问题分析

将一个数组分成两个,使两个数组的和之差最小,要想差最小,必然a组的和 b组的和 都趋近sum/2,换句话说,a组和在sum/2的左边逼近,b组和在sum/2的右边逼近。当然,a组的和逼近sum/2时,b组和也必然逼近。 我们把数字想象成石头,数字大小代表石头重量,所以,这个问题可以转化为在a组的总重量 <= sum/2前提下,从原数组中拿取石头,使a组的总重量最大,即简化版的背包问题。

建立状态转移方程

f(i,target) 表示 轮到第i个石头选择时,背包最大空间为target时能获得的最大物品重量,

设数组weight[n] 表示每个石头的重量,

则状态转移方程如下 f(i,target) = Max(f(i-1,target), f(i,target-weight[i])+weight[i])

f(i-1,target) 表示第i个物品不选时,能获得的最大重量

f(i,target-weight[i])+weight[i] 表示第i个物品选择时,能获得的最大重量

代码实现

/**
 * @param stones 待分组的石头
 * @return a,b两组的最小差值
 */
public int getMinSub(int[] stones) {
    if (stones == null || stones.length == 0) {
        return 0;
    }
    //石头总数
    int size = stones.length;
    //所有石头的总重量
    int sumWeight = 0;
    for (int temp : stones) {
        sumWeight = sumWeight + temp;
    }
    //所需背包的最大承受重量为所有石头总重量的一半
    int maxWeight = sumWeight / 2 + 1;
    //定义一个二维数组表示 轮到第i个石头选择时,背包最大承重为maxWeight时能装的石头最大总重量
    int[][] data = new int[size + 1][maxWeight];
    //从第一块石头开始遍历
    for (int i = 1; i <= size; i++) {
        //w代表背包的最大承受重量
        for (int w = 0; w < maxWeight; w++) {
            //第i个石头不选
            int temp1 = data[i - 1][w];
            int temp2 = 0;
            //第i个石头选了,选之前要先看背包放不放得下
            if (stones[i - 1] <= w) {
                temp2 = data[i - 1][w - stones[i - 1]] + stones[i - 1];
            }
            //每块石头都可以选和不选,取最大值
            data[i][w] = Math.max(temp1, temp2);
        }
    }
    //a,b两组求差
    return Math.abs(sumWeight - data[size][maxWeight - 1] - data[size][maxWeight - 1]);
}