动态规划算法

介绍

1、核心思想:将大问题分为小问题进行解决,从而一步步获取最优的处理算法,与分治算法类似

2、与分治算法不同的是,适合于用动态规划求解的问题,经分解得到的子问题往往不是互相独立的,即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解

3、动态规划可以通过填表的方式来逐步推进,得到最优解

 

背包问题

1、一个给定容量的背包、若干具有一定价值和重量的物品,如何选择物品放入背包使物品的价值最大

2、类型

(1)0-1 背包问题:每件物品都不可再分,要么整个装入背包,要么放弃,不允许出现类似“将物品的 1 / 3 装入背包”的情况

(2)部分背包问题:每件物品是可再分的,即允许将某件物品的一部分(例如 1 / 3)放入背包

(3)完全背包问题:挑选物品时,每件物品可以选择多个,也就是说不限物品的数量

(4)多重背包问题:每件物品的数量是有严格规定的,比如物品 A 有 2 件,物品 B 有 3 件

3、完全背包可以转化为 01 背包

 

0-1 背包

1、假设

(1)给定的 n 个物品

(2)设 value[i]、weight[i]分别为第 i 个物品的价值和重量

(3)maxValue[i][m] 表示在前 i 个物品中能够装入容量为 m 的背包中的最大价值

2、条件

(1)maxValue[i][0] = maxValue[0][m] = 0:maxValue[i][0] = 0,容量为0的背包最大价值是0;maxValue[0][m] = 0,不加入物品

(2)当weight[i] > m时,maxValue[i][m] = maxValue[i - 1][m]:新增物品在当前容量装不下,直接使用上一个装入策略

(3)当weight[i] >= m时, maxValue[i][m] = max{maxValue[i - 1][m],value[i] + maxValue[i - 1][m - weight[i]]}:放入新增物品,以子问题的解,利用背包剩余空间

 

代码实现

public class Knapsack {//01背包
    int[] weight;//物品的重量
    int[] value;//物品的价值
    int capacity;//背包容量
    int number;//物品的个数
    int[][] table;//表格记录在前n个物品中能够装入容量为m的背包中的最大价值
    int[][] path;//记录新增物品在当前背包容量下是否被放入背包

    //构造器,物品重量、价值的下标要相等
    public Knapsack(int[] weight, int[] value, int capacity) {
        this.weight = weight;
        this.value = value;
        this.capacity = capacity;
        this.number = value.length;
        this.table = new int[number + 1][capacity + 1];
        this.path = new int[number + 1][capacity + 1];
    }

    //代码块可省略
    {
        //初始化表格第一列:容量为0的背包
        for (int i = 0; i < table.length; i++) {
            table[i][0] = 0;
        }
        //初始化表格第一行:不加入物品
        for (int i = 0; i < table[0].length; i++) {
            table[0][i] = 0;
        }
    }

    //动态规划处理
    public void dynamicProgram() {
        for (int i = 1; i < table.length; i++) {//不处理第一行
            for (int j = 1; j < table[i].length; j++) {//不处理第一列
                if (weight[i - 1] > j) {
                    table[i][j] = table[i - 1][j];//放入的物品重量超过背包容量,直接采用上一个单元格的最优解
                } else {//weight[i - 1] <= j,加入新增物品i
                    if (table[i - 1][j] < value[i - 1] + table[i - 1][j - weight[i - 1]]) {
                        table[i][j] = value[i - 1] + table[i - 1][j - weight[i - 1]];
                        path[i][j] = 1;//在背包容量为j时新增物品i时,产生新的最优解,将此时的情况做一个标记
                    } else {
                        table[i][j] = table[i - 1][j];//在背包容量为j时新增物品i时,没有产生最优解,直接采用上一个单元格的最优解
                    }
                }
            }
        }
    }

    //输出表格
    public void printTable() {
        for (int i = 0; i < table.length; i++) {
            for (int j = 0; j < table[i].length; j++) {
                System.out.print(table[i][j] + "\t");
            }
            System.out.println();
        }
    }

    //从后往前遍历,因为最终的最优解在最后面
    public void optimalSolution() {
        int i = path.length - 1;
        int j = path[0].length - 1;
        while (i > 0 && j > 0) {
            if (path[i][j] == 1) {//说明这个物品被放入过
                System.out.printf("放入了第%d个物品\n", i);
                j -= weight[i - 1];//利用剩余容量继续找其他物品
            }
            i--;
        }
    }
}

 

posted @   半条咸鱼  阅读(69)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示