【动态规划法】之01背包问题java实现

问题描述

给定n种物品和一个背包。物品i的重量是Wi,其价值为Vi背包的容量为C。问如何选择装入背包中的物品,使得装入背包中物品的总价值最大?
在这里插入图片描述01背包问题的目标是使背包中物品的价值最大。约束条件有两个:一是物品的重量不能超出背包的容量。二是物品只能整体放入背包,不能部分放入。

问题分析

(1)最优子结构
在这里插入图片描述反证法
设(y1,y2,……,yn)是所给01背包问题的一个最优解,则(y2,y3,……,yn)是子问题的一个最优解。子问题满足以下条件:一是物品的总价值是物品y2,y3,……,yn的总价值和;二是物品的总重量小于等于C-(w1y1),否则。设(Z1,Z2,……,Zn)是上述子问题的一个最优解,则(y1,z1,Z2……,yn)是比(y1,y2,……,yn)更优的解。(矛盾)
故01背包问题具有最优子结构性质。

构造01背包问题的递归结构
设所给0-1背包的子问题的最优值为m(i,j),即背包容量为j,可选择物品为i,i+1……n时的最优值,通过递归关系求得最优值表m。
在这里插入图片描述对01背包问题的队规关系分两种情况。
装入物品n的情况下可分为两种情况,若物品n的重量大于背包的容量时,无法装入物品n,若物品n的重量小于等于背包的容量时,装入物品n
在这里插入图片描述二是装入的可选物品m(i,j),物品无法装入时,即物品i的重量大于背包剩余容量的情况下;能够装入物品,即物品i的重量小于等于背包剩余容量的情况。
在这里插入图片描述java代码

package com.xiang;

import java.util.Scanner;
 
/**
 * 使用二维数组非递归的方法求解0/1背包问题
 */
public class ZeroOnePack {
    // N表示物体的个数,V表示背包的载重
    int N,V;
    //用于存储每个物体的重量,下标从1开始
    private int[] weight;
    //存储每个物体的收益,下标从1开始
    private int[] value;
    //二维数组,用来保存每种状态下的最大收益
    private int[][] F;
 
    /**
     * 使用非递归方式,求解F[0 .. N][0 .. V],即for循环从下至上求解
     */
    public void ZeroOnePackNonRecursive() {
        //对二维数组F进行初始化
        for(int j = 0; j <= V; j++) {
            F[0][j] = 0;
        }
 
        //注意边界问题,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];
                }
            }
        }
 
        //打印所有结果,我们要求的是F[N][V]
        for(int i = 0; i <= N; i++) {
            for(int j = 0; j <= V; j++) {
                System.out.print(F[i][j] + " ");
            }
            System.out.println();
        }
    }
 
 
    /**
     * 求解F[n][m]这个最优值具体选取哪几样物品能获得最大价值,但只会输出一种情况
     * @param n     表示前n个物体,n <= N
     * @param v     表示背包的容量,v <= V
     */
    public void printResult(int n, int v) {
        boolean[] isAdd = new boolean[n + 1];
 
        for(int i = n; i >= 1; i--) {
            if(F[i][v] == F[i-1][v])
                isAdd[i] = false;
            else {
                isAdd[i] = true;
                v -= weight[i];
            }
        }
 
        for(int i = 1; i <= n; i++) {
            System.out.print(isAdd[i] + " ");
        }
        System.out.println();
    }
 
    /**
     * 输入格式:
     5 10
     2 2 6 5 4
     6 3 5 4 6
     * result:15
     * 第一行是物体个数、背包总空间;
     * 第二行是每个物体的空间;
     * 第三行是每个物体的收益。
     */
    public void init() {
        Scanner sc = new Scanner(System.in);
        N = sc.nextInt();
        V = sc.nextInt();
 
        //下标从1开始,表示第1个物品
        weight = new int[N + 1];
        value = new int[N + 1];
        F= new int[N + 1][V + 1];//注意是 N + 1,因为需要一个初始状态F[0][0],表示前0个物品放进空间为0的背包的最大收益
 
        for(int i = 1; i <= N; i++) {
            weight[i] = sc.nextInt();
        }
 
        for(int i = 1; i <= N; i++) {
            value[i] = sc.nextInt();
        }
    }
 
    public static void main(String[] args) {
        ZeroOnePack zop = new ZeroOnePack();
        zop.init();
        zop.ZeroOnePackNonRecursive();
        zop.printResult(zop.N,zop.V);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值