动态规划算法
介绍
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--;
}
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战