背包(01背包,背包装满,完全背包)问题

先给一段代码熟悉一下最简单的背包

解法代码框架
import java.util.Scanner;

public class pack {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int n;
        //物品个数
        n=scanner.nextInt();
        int dv=scanner.nextInt();
        //体积
        int[] v=new int[n];
        //价值
        int[] w=new int[n];
        for(int i=0;i<n;i++){
            v[i]=scanner.nextInt();
        }
        for(int i=0;i<n;i++){
            w[i]=scanner.nextInt();
        }
        int maxv[][]=new int[n+1][dv+1];
        for(int i=0;i<=n;i++){
            for(int j=0;j<=dv;j++){
                if(i==0|| j==0){
                    maxv[i][j]=0;
                    continue;
                }
                //背包体积小于物品体积
                if(j<v[i-1]) maxv[i][j]=maxv[i-1][j];
                //背包体积大于等于物品体积
                else {
                	//判断是否要将该物品添加进去,添加的话就要清除出该物品的体积的空位出来
                    int add=w[i-1]+maxv[i-1][j-v[i-1]];
                    int not=maxv[i][j]=maxv[i-1][j];
                    maxv[i][j]=add>not?add:not;
                }
            }
        }
        for(int i=0;i<=n;i++){
            for(int j=0;j<=dv;j++) {
                System.out.print(maxv[i][j]+" ");
            }
            System.out.println();

        }
    }

}

1—01背包

问题:m件物品,承重为n

1.我们会定义两个一维数组,一个是物体重量,一个是物体价值。

还有一个二维数组表示在i件物品和背包为j的情况下的最大价值行为i=(m+1),列为j=(n+1)。i表示可以拿前面i件物品,j表示背包可装载容量
然后初始化在它只能装入0件物品的时候,各个承重的背包可以获得的价值。(第一行初始化为0价值)

2.然后做循环i,j

		// 注意边界问题,i是从1开始的,j是从0开始的
		// 因为F[i - 1][j]中i要减1
		for (int i = 1; i <= N; i++) {
			for (int j = 0; j <= V; j++) {
				// 如果容量为j的背包放得下第i个物体
				if (j >= weight[i]) {
					F[i][j] = Math.max(F[i - 1][j - weight[i]] + value[i], F[i - 1][j]);
				} else {
					// 放不下,只能选择不放第i个物体
					F[i][j] = F[i - 1][j];
				}
			}
		}
		

2—01背包&&恰好完全装满

还有一种问题:要求背包恰好装满的情况。

在对二维数组初始化时:
1)v[0][0]初始化为0
2)第一行和第一列的其他值都设为-INF(未装满的状态)

原因:
1)占用空间为0的物品刚好可以装满空间为0的背包
2)只有上一层恰好装满时,使用状态方程得到的下一层才能是正好装满,因此初始值设为-INF,即上一层未装满时,下一层加入a[i]结果仍然是-INF即未装满。

同样的,初始化一维数组时,将v[0]设为0,其他设为-INF

3—完全背包

道理差不多,就是在加入新物体的过程中,再加一个循环来看看要加多少个,这里的增加是在二维数组中对该值进行修正。


        //注意边界问题,i是从1开始的
        for(int i = 1; i <= N; i++) {
            //j是正序、降序没影响
            for (int j = 0; j <= V; j++) {
                for (int k = 0; k <= V / weight[i]; k++) {
                    if (j >= k * weight[i]) {
                        //注意:状态转移方程是F[i][j],而不是F[i - 1][j]
                        //因为这时放k个第i个物品,之后还可能继续放这个物体,所以应是F[i][j]
                        F[i][j] = Math.max(F[i - 1][j - k * weight[i]] + k * value[i], F[i][j]);
                    } else {
                        //可以省略,这里为什么不是F[i - 1][j]
                        //因为刚开始k=0,j >= 0 * weight[i]肯定成立,此时F[i][j] = F[i - 1][j]。
                        F[i][j] = F[i][j];
                    }
                }
            }
        }

另一种写法,空间优化

//这里的做法是把原本的二维数组变成一个一维数组。
//但是原理一样
//就是得到Math.max((承载重量为j-weight[i]的最大价值+该物品价值),该承重的原本的价值)
        //注意边界问题,i是从1开始的
        for(int i = 1; i <= N; i++) {
            //唯一不同的地方,j是正序遍历
            for(int j = 0; j <= V; j++) {
                if(j >=  weight[i]) {
                    F[j] = Math.max(F[j - weight[i]] + value[i], F[j]);
                }else {
                    //可以省略
                    F[j]= F[j];
                }
            }
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值