动态规划解决0-1背包问题

需求:对于一组不同重量、不可分割的物品,我们需要选择一些装入背包,在满足背包最大重量限制的前提下,背包中物品总重量的最大值是多少呢?
之所以叫0-1背包问题,就是因为物品不可分割。

动态规划的实现逻辑是根据前面计算的状态结果来决定后续计算,但是不考虑之前的结果是如何计算的,既然要根据前面的状态,那么就要把前面计算的状态结果保存下来,这就需要申请额外的空间。
动态规划也有公式,就跟递归有递归公式一样,写动态规划就是写动态规划公式。
简单来说动态规划的重点:
1、基于前面的计算结果来进行后续计算。
2、编写动态规划公式。

我们来看代码,理解一下上面的理论。

/**
 * 背包0-1问题 动态规划
 * 
 */
public class BeiBao_DongTaiGuiHua {

    // 物品重量
    private static final int[] weight = {2,2,4,6,3};

    // 物品个数
    private static final int n = 5;

    // 背包承受的最大重量
    private static final int w = 9;

    public static void main(String[] args) {
        BeiBao_DongTaiGuiHua.getMaxWeight();
    }

    public static void getMaxWeight() {
        //定义标记数组
        boolean[][] wFlag = new boolean[n][w+1];

        //初始化第一个物品的标记结果
        wFlag[0][0] = true;
        if(weight[0]<w) wFlag[0][weight[0]] = true;

        //动态规划计算背包结果
        for(int x=1; x<n; x++) {
            //第x个商品不放进背包
            for(int y=0; y<=w; y++) {
                if(wFlag[x-1][y]) {
                    wFlag[x][y] = wFlag[x-1][y];
                }
            }

            //第x个商品放进背包
            for(int y=0; y<=w-weight[x]; y++) {
                System.out.println("当前第:"+(x+1)+"个商品,当前计算位置:"+y);
				
				//这里就是动态规划的公式
                if(wFlag[x-1][y]) {
                    wFlag[x][y+weight[x]] = true;
                }
            }

            System.out.println("------------------------------------------");
        }

        //打印结果
        for(int x=w; x>=0; x--) {
            if(wFlag[n-1][x]) {
                System.out.println("背包中最大重量为:" + x);
                return;
            }
        }

    }
}

/* 输出
当前第:2个商品,当前计算位置:0
当前第:2个商品,当前计算位置:1
当前第:2个商品,当前计算位置:2
当前第:2个商品,当前计算位置:3
当前第:2个商品,当前计算位置:4
当前第:2个商品,当前计算位置:5
当前第:2个商品,当前计算位置:6
当前第:2个商品,当前计算位置:7
------------------------------------------
当前第:3个商品,当前计算位置:0
当前第:3个商品,当前计算位置:1
当前第:3个商品,当前计算位置:2
当前第:3个商品,当前计算位置:3
当前第:3个商品,当前计算位置:4
当前第:3个商品,当前计算位置:5
------------------------------------------
当前第:4个商品,当前计算位置:0
当前第:4个商品,当前计算位置:1
当前第:4个商品,当前计算位置:2
当前第:4个商品,当前计算位置:3
------------------------------------------
当前第:5个商品,当前计算位置:0
当前第:5个商品,当前计算位置:1
当前第:5个商品,当前计算位置:2
当前第:5个商品,当前计算位置:3
当前第:5个商品,当前计算位置:4
当前第:5个商品,当前计算位置:5
当前第:5个商品,当前计算位置:6
------------------------------------------
背包中最大重量为:9
 */

通过计算我们看到输出的结果是9,动态规划的计算公式是:

 if(wFlag[x-1][y] == true)
      wFlag[x-1][y+weight[x] = true

标记状态的数据wFlag的状态推演:

初始状态
0   0   0   0   0   0   0   0   0   0
0   0   0   0   0   0   0   0   0   0
0   0   0   0   0   0   0   0   0   0
0   0   0   0   0   0   0   0   0   0
0   0   0   0   0   0   0   0   0   0


第一个物品不放和放入背包(下标为0的物品)
1   0   1   0   0   0   0   0   0   0
0   0   0   0   0   0   0   0   0   0
0   0   0   0   0   0   0   0   0   0
0   0   0   0   0   0   0   0   0   0
0   0   0   0   0   0   0   0   0   0


第二个物品不放和放入背包(下标为1的物品)
1   0   1   0   0   0   0   0   0   0
1   0   1   0   1   0   0   0   0   0
0   0   0   0   0   0   0   0   0   0
0   0   0   0   0   0   0   0   0   0
0   0   0   0   0   0   0   0   0   0


第三个物品不放和放入背包(下标为2的物品)
1   0   1   0   0   0   0   0   0   0
1   0   1   0   1   0   0   0   0   0
1   0   1   0   1   0   1   0   1   0
0   0   0   0   0   0   0   0   0   0
0   0   0   0   0   0   0   0   0   0

第四和第五以此类推…

动态规划的代码中我们发现使用了一个二维数组,我们其实还可以对动态规划的计算空间进行优化,将二维数组改为一维数组。
代码如下:

/**
 * 优化标记空间,从二维数据变为一维数组
 */
public static void getMaxWeight2() {
	//定义标记数组
	boolean[] wFlag = new boolean[w+1];

	//初始化第一个物品的标记结果
	wFlag[0] = true;
	if(weight[0]<w) wFlag[weight[0]] = true;

	//动态规划计算背包结果
	for(int x=1; x<n; x++) {

		//第x个商品放进背包
		for(int y=0; y<=w-weight[x]; y++) {
			System.out.println("当前第:"+(x+1)+"个商品,当前计算位置:"+y);
			if(wFlag[y]) {
				wFlag[y+weight[x]] = true;
			}
		}

		System.out.println("------------------------------------------");
	}

	//打印结果
	for(int x=w; x>=0; x--) {
		if(wFlag[x]) {
			System.out.println("背包中最大重量为:" + x);
			return;
		}
	}

}