学习日志---动态规划(背包问题)

背包问题:

利用动态矩阵的方式,一步一步在前一次有最优解的时候,推断后面的最优解

核心:

bestValues[i][j] = Math.max(bestValues[i - 1][j],
                                ivalue + bestValues[i - 1][j - iweight]);

i是指商品,[i]是指前i个,v是指背包可容纳的重量,节点值是指最优的价值数。

因为加了一个i,最优解要么就是有i,要么就是没有i。

摘自:http://www.cnblogs.com/bourbon/archive/2011/08/23/2151044.html

如果一个问题的最优解包含了物品n,即Xn = 1,那么其余X1, X2, .....,Xn-1 一定构成子问题1,2,.....,n-1在容量C - cn时的最优解。如果这个最优解不包含物品n,即Xn = 0;

那么其余 X1, X2.....Xn-1一定构成了子问题 1,2,....n-1在容量C时的最优解。

//请各位仔细品味这几句话

//物品类
public class Knapsack {

    /** 物品重量 */
    private int weight;

    /** 物品价值 */
    private int value;

    /***
     * 构造器
     */
    public Knapsack(int weight, int value) {
        this.value = value;
        this.weight = weight;
    }

    public int getWeight() {
        return weight;
    }

    public int getValue() {
        return value;
    }

    public String toString() {
        return "[weight: " + weight + " " + "value: " + value + "]";
    }
}

import java.util.ArrayList;

//背包算法类
public class KnapsackProblem {

    /** 指定背包 */
    private Knapsack[] bags;

    /** 总承重 */
    private int totalWeight;

    /** 给定背包数量 */
    private int n;

    /** 前 n 个背包,总承重为 totalWeight 的最优值矩阵 */
    private int[][] bestValues;

    /** 前 n 个背包,总承重为 totalWeight 的最优值 */
    private int bestValue;

    /** 前 n 个背包,总承重为 totalWeight 的最优解的物品组成 */
    private ArrayList<Knapsack> bestSolution;

    public KnapsackProblem(Knapsack[] bags, int totalWeight) {
        this.bags = bags;
        this.totalWeight = totalWeight;
        this.n = bags.length;
        if (bestValues == null) {
            bestValues = new int[n + 1][totalWeight + 1];
        }
    }

    /**
     * 求解前 n 个背包、给定总承重为 totalWeight 下的背包问题
     * 
     */
    public void solve() {

        System.out.println("给定背包:");
        for (Knapsack b : bags) {
            System.out.println(b);
        }
        System.out.println("给定总承重: " + totalWeight);

        // 求解最优值
        for (int j = 0; j <= totalWeight; j++) {
            for (int i = 0; i <= n; i++) {

                if (i == 0 || j == 0) {
                    bestValues[i][j] = 0;
                } else {
                    // 如果第 i 个背包重量大于总承重,则最优解存在于前 i-1 个背包中,
                    // 注意:第 i 个背包是 bags[i-1]
                    if (j < bags[i - 1].getWeight()) {
                        bestValues[i][j] = bestValues[i - 1][j];
                    } else {
                        // 如果第 i 个背包不大于总承重,则最优解要么是包含第 i 个背包的最优解,
                        // 要么是不包含第 i 个背包的最优解, 取两者最大值,这里采用了分类讨论法
                        // 第 i 个背包的重量 iweight 和价值 ivalue
                        int iweight = bags[i - 1].getWeight();
                        int ivalue = bags[i - 1].getValue();
                        bestValues[i][j] = Math.max(bestValues[i - 1][j],
                                ivalue + bestValues[i - 1][j - iweight]);
                    } // else
                } // else
            } // for
        } // for

        // 求解背包组成
        //这里是从矩阵中找最优的包裹,从后面开始找
        if (bestSolution == null) {
            bestSolution = new ArrayList<Knapsack>();
        }
        int tempWeight = totalWeight;
        for (int i = n; i >= 1; i--) {
            if (bestValues[i][tempWeight] > bestValues[i - 1][tempWeight]) {
                bestSolution.add(bags[i - 1]); // bags[i-1] 表示第 i 个背包
                tempWeight -= bags[i - 1].getWeight();
            }
            if (tempWeight == 0) {
                break;
            }
        }
        bestValue = bestValues[n][totalWeight];
    }

    /**
     * 获得前 n 个背包, 总承重为 totalWeight 的背包问题的最优解值 调用条件: 必须先调用 solve 方法
     * 
     */
    public int getBestValue() {
        return bestValue;
    }

    /**
     * 获得前 n 个背包, 总承重为 totalWeight 的背包问题的最优解值矩阵 调用条件: 必须先调用 solve 方法
     * 
     */
    public int[][] getBestValues() {

        return bestValues;
    }

    /**
     * 获得前 n 个背包, 总承重为 totalWeight 的背包问题的最优解值矩阵 调用条件: 必须先调用 solve 方法
     * 
     */
    public ArrayList<Knapsack> getBestSolution() {
        return bestSolution;
    }

}

public class KnapsackTest {

    public static void main(String[] args) {

        Knapsack[] bags = new Knapsack[] { new Knapsack(2, 13),
                new Knapsack(1, 10), new Knapsack(3, 24), new Knapsack(2, 15),
                new Knapsack(4, 28), new Knapsack(5, 33), new Knapsack(3, 20),
                new Knapsack(1, 8) };
        int totalWeight = 12;
        KnapsackProblem kp = new KnapsackProblem(bags, totalWeight);

        kp.solve();
        System.out.println(" -------- 该背包问题实例的解: --------- ");
        System.out.println("最优值:" + kp.getBestValue());
        System.out.println("最优解【选取的背包】: ");
        System.out.println(kp.getBestSolution());
        System.out.println("最优值矩阵:");
        int[][] bestValues = kp.getBestValues();
        for (int i = 0; i < bestValues.length; i++) {
            for (int j = 0; j < bestValues[i].length; j++) {
                System.out.printf("%-5d", bestValues[i][j]);
            }
            System.out.println();
        }
    }
}
时间: 2024-11-08 22:29:06

学习日志---动态规划(背包问题)的相关文章

oj算法----动态规划----背包问题

oj算法----动态规划----背包问题 1.动态规划 1.1概念 动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法 1.2性质 动态规划一般用来处理最优解的问题.使用动态规划算法思想解决的问题一般具有最优子结构性质和重叠子问题这两个因素. <1> 最优子结构 一个问题的最优解包含其子问题的最优解,这个性质被称为最优子结构性质 <2> 重叠子问题 递归算法求解问题时,每次产生的子问题并不总是新问

winform学习日志(二十三)---------------socket(TCP)发送文件

一:由于在上一个随笔的基础之上拓展的所以直接上代码,客户端: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Net.Sockets; using Sys

Linux学习日志2-vim使用基础

vim是linux操作系统下的一个文本编辑工具,功能非常强大,但刚学习起来比较复杂.vim的所有功能要讲明白得有几百页,在这里只是记录一下vim的一些基本用法. 首先vim打开文件的三种方式: vim +# xxx(#是数字):表示打开xxx文件并将光标定位到指定行. vim -o xx1 xx2 xx3:表示同时打开三个文件,垂直分割显示 vim -O xx1 xx2 xx3:表示同时打开三个文件,水平分割显示 多个文件间跳转:键入ctrl+w后:→向左.←向右.↑向上.↓向下 vim打开文件

Linux学习日志day1——无人值守系统安装DHCP+TFTP+PXE+Kickstar

Linux学习日志day1--无人值守批量系统远程网络安装(DHCP+TFTP+PXE+Kickstar)                                         --作者:江信瀚 服务器环境介绍: 主机名:workstation.example.com 关闭SElinux以及防火墙 虚拟机:VMware(关闭了VMware的DHCP服务) 网卡配置: 静态IP获取! IPV6全部都删除,因为根本用不到 子网IP可以在VMware中设置 8.8.8.8是谷歌的DNS服务器

Cocos2d-x 3.1.1 学习日志4--cocos2d-x解决中文乱码问题的几种办法

做个打飞机的游戏,由于版本太新,网上基本没有教教程,我的版本是cocos2d-x 3.1.1的,今天遇到cocos2dx中中文乱码的问题.无奈只好Google百度寻求答案,明白了这个问题的缘由.因为cocos2d-x内部是以utf8处理文本的,而VS直接输入时文本编码为GBK,如果添加L标志,则为Unicode编码. 解决这个问题有三种办法: 将源代码文件保存为utf8编码,不过由于编译器的问题,这种方式会导致很多无法预测的问题 将字符串用utf8编码集中存到一文件中,然后用代码读取这些字符串来

Cocos2d-x 3.1.1 学习日志3--C++ 初始化类的常量数据成员、静态数据成员、常量静态数据成员

有关const成员.static成员.const static成员的初始化: 1.const成员:只能在构造函数后的初始化列表中初始化 2.static成员:初始化在类外,且不加static修饰 3.const static成员:类只有唯一一份拷贝,且数值不能改变.因此,可以在类中声明处初始化,也可以像static在类外初始化 #include <iostream> using std::cout; using std::endl; class base { public: base(int

SQL 学习日志01

查看一个数据库的所有表: Select TABLE_NAME FROM 数据库名称.INFORMATION_SCHEMA.TABLES Where TABLE_TYPE='BASE TABLE' (select * from 表名 where 条件) 查看一张表的表结构: sp_help table_name(表名)  获取的信息比较全 sp_columns table_name(表名) 创建数据库: use master go create database test01(数据库名) 删除数据

SQL 学习日志02

SQL数据类型 1.字符类型 char   --定长字符数据   如 char(12)  这字段就会占用12字节的空间,无论这个字段只填写了2个字节.一般在可确定这字段长度时选用,如sex字段(因只有男和女两项可选)就可用 char(2). varvhar   --可变长字符数据  如varchar(50) 这字段最大只能填写50字节,按实际填写的字节存储.一般在不确定这字段长度时使用,如 Smail字段(因邮箱的长度不确定) 就可用varchar(50). text    --用来存储大量非统

Linux学习日志1-基本知识

1.冯.诺依曼体系计算机五大组成部件: 1.控制器:控制其他四个部件的运作 2.运算器:负责计算加减乘除 3.存储器:存放运算的数据来源与结果 4.输入设备:接收数据输入存入存储器 5.输出设备:从存储器接收数据输出 2.Linux的起源: 1991年一个芬兰大学生Linus Torvalds参考其老师的教学用操作系统Minix的思想(注意仅仅是思想),自己写了一个操作系统内核,命名为Linux 0.0.1,发布在comp.os.minix新闻组上,正式宣告Linux内核的诞生.从那时起,Lin