回溯算法_01背包问题_Java实现
原文地址:http://blog.csdn.net/ljmingcom304/article/details/50314839
本文出自:【梁敬明的博客】
1.回溯算法
回溯算法也叫试探法,通俗的将就是一个方向的路一直往前走,能走则走,不能走则退回来换一个方向再试。一般的实现步骤是:针对一个问题定义解的空间,至少包含问题的一个最优解;用易于搜索的解空间结构,使得能用回溯方法搜索整个解空间;以深度优先的方式搜索整个解空间,并在搜索过程中通过剪枝函数避免无效搜索。
回溯算法
如上图所示,每个节点均有左右两种选择,最终要实现结果会产生AH、AI、AJ、AK、AL、AM、AN和AO八种实现方案,需要确定哪种方案为最优方案。以左边的条件为首要条件进行判断,那么其搜索路线为:【A】→【B】→【D】→【H】→【I】→【E】→【J】→【K】→【C】→【F】→【L】→【M】→【G】→【N】→【O】,即在全部满足限制条件的前提下,实现每个节点之间的两两比较,最终选择出最优方案。
2.背包问题
一个旅行者有一个最多能装m公斤的背包,现在有n中物品,每件的重量分别是W1、W2、……、Wn,每件物品的价值分别为C1、C2、……、Cn, 需要将物品放入背包中,要怎么样放才能保证背包中物品的总价值最大?
3.算法分析
上述问题包含两种情况,一种是将物品放入背包中,另一种是不将物品放入背包中。其中隐含着三个限制条件。第一个限制条件为放入物品的总和不能超过背包的总承重,即当放入的物品总和超过背包的总承重时,即使背包中物品的总价值再大也毫无意义。第二个限制条件为要保证背包中物品的总价值最大。无论第一个条件成不成立,第二个条件总是在其之上的一种更优的选择。第三个限制条件为当达到物品的数量上限,即没有可以获取物品再放入背包时,前两个限制条件均毫无意义,因此需要优先进行判断。
这里我们可以将第一种情况看做左节点,第二种情况看做右节点,第三个限制条件用于终结节点的搜索。因此在解决该问题时,首先需要判断是否有物品可以放到背包中,要是没有物品可以放到背包中,直接返回结果,当前的价值即为背包的最优价值。然后判断左节点是否为可行节点,当左节点可行,就优先搜索左子树,不满足则进入右子树。当达到物品的数量上限,直接返回当前物品数量的结果;若左右节点同时不可行,则直接将结果返回上一步。以此类推,将结果从下至上一层一层进行返回,得到问题的最优结果。
创建一个物品对象,分别存在价值、重量以及单位重量价值三种属性。
public class Knapsack implements Comparable<Knapsack> {
/** 物品重量 */
private int weight;
/** 物品价值 */
private int value;
/** 单位重量价值 */
private int unitValue;
public Knapsack(int weight, int value) {
this.weight = weight;
this.value = value;
this.unitValue = (weight == 0) ? 0 : value / weight;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public int getUnitValue() {
return unitValue;
}
@Override
public int compareTo(Knapsack snapsack) {
int value = snapsack.unitValue;
if (unitValue > value)
return 1;
if (unitValue < value)
return -1;
return 0;
}
}
按照回溯算法将物品放入背包中。
public class HSSFProblem {
// 待选择的物品
private Knapsack[] bags;
// 背包的总承重
private int totalWeight;
// 背包的当前承重
private int currWeight;
// 待选择物品数量
private int n;
// 放入物品后背包的最优价值
private int bestValue;
// 放入物品和背包的当前价值
private int currValue;
public HSSFProblem(Knapsack[] bags, int totalWeight) {
this.bags = bags;
this.totalWeight = totalWeight;
this.n = bags.length;
// 物品依据单位重量价值从大到小进行排序
Arrays.sort(bags, Collections.reverseOrder());
}
public int solve(int i) {
// 当没有物品可以放入背包时,当前价值为最优价值
if (i >= n) {
bestValue = currValue;
return bestValue;
}
// 首要条件:放入当前物品,判断物品放入背包后是否小于背包的总承重
if (currWeight + bags[i].getWeight() <= totalWeight) {
// 将物品放入背包中的状态
currWeight += bags[i].getWeight();
currValue += bags[i].getValue();
// 选择下一个物品进行判断
bestValue = solve(i + 1);
// 将物品从背包中取出的状态
currWeight -= bags[i].getWeight();
currValue -= bags[i].getValue();
}
// 次要条件:不放入当前物品,放入下一个物品可能会产生更优的价值,则对下一个物品进行判断
// 当前价值+剩余价值<=最优价值,不需考虑右子树情况,由于最优价值的结果是由小往上逐层返回,
// 为了防止错误的将单位重量价值大的物品错误的剔除,需要将物品按照单位重量价值从大到小进行排序
if (currValue + getSurplusValue(i + 1) > bestValue) {
// 选择下一个物品进行判断
bestValue = solve(i + 1);
}
return bestValue;
}
// 获得物品的剩余总价值
public int getSurplusValue(int i) {
int surplusValue = 0;
for (int j = i; j < n; j++)
surplusValue += bags[i].getValue();
return surplusValue;
}
}
最终测试结果:90
public class HSSFTest {
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;
HSSFProblem problem = new HSSFProblem(bags, totalWeight);
System.out.println(problem.solve(0));
}
}
欢迎关注我的微信公众号:“”Java面试通关手册(坚持原创,分享美文,分享各种Java学习资源,面试题,以及企业级Java实战项目回复关键字免费领取):
回溯算法_01背包问题_Java实现的更多相关文章
- 贪心算法_01背包问题_Java实现
原文地址:http://blog.csdn.net/ljmingcom304/article/details/50310789 本文出自:[梁敬明的博客] 1.贪心算法 什么是贪心算法?是指在对问题进 ...
- 动态规划_01背包问题_Java实现
原文地址:http://blog.csdn.net/ljmingcom304/article/details/50328141 本文出自:[梁敬明的博客] 1.动态规划 什么是动态规划?动态规划就是将 ...
- LeetCode:回溯算法
回溯算法 这部分主要是学习了 labuladong 公众号中对于回溯算法的讲解 刷了些 leetcode 题,在此做一些记录,不然没几天就忘光光了 总结 概述 回溯是 DFS 中的一种技巧.回溯法采用 ...
- 46. Permutations 回溯算法
https://leetcode.com/problems/permutations/ 求数列的所有排列组合.思路很清晰,将后面每一个元素依次同第一个元素交换,然后递归求接下来的(n-1)个元素的全排 ...
- ACM/ICPC 之 最长公共子序列计数及其回溯算法(51Nod-1006(最长公共子序列))
这道题被51Nod定为基础题(这要求有点高啊),我感觉应该可以算作一级或者二级题目,主要原因不是动态规划的状态转移方程的问题,而是需要理解最后的回溯算法. 题目大意:找到两个字符串中最长的子序列,子序 ...
- c语言数据结构:递归的替代-------回溯算法
1.要理解回溯就必须清楚递归的定义和过程. 递归算法的非递归形式可采用回溯算法.主要考虑的问题在于: 怎样算完整的一轮操作. 执行的操作过程中怎样保存当前的状态以确保以后回溯访问. 怎样返回至上一次未 ...
- 8皇后以及N皇后算法探究,回溯算法的JAVA实现,非递归,循环控制及其优化
上两篇博客 8皇后以及N皇后算法探究,回溯算法的JAVA实现,递归方案 8皇后以及N皇后算法探究,回溯算法的JAVA实现,非递归,数据结构“栈”实现 研究了递归方法实现回溯,解决N皇后问题,下面我们来 ...
- 8皇后以及N皇后算法探究,回溯算法的JAVA实现,递归方案
八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例.该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同 ...
- 回溯算法之n皇后问题
今天在看深度优先算法的时候,联想到DFS本质不就是一个递归回溯算法问题,只不过它是应用在图论上的.OK,写下这篇博文也是为了回顾一下回溯算法设计吧. 学习回溯算法问题,最为经典的问题我想应该就是八皇后 ...
随机推荐
- 解决Lenovo(ldeapad)笔记本F1-F12功能键操作无效的问题
1.操作条件:Lenovo笔记本自带的“一键恢复”按钮 2.操作方法:上下键为切换选项,回车键(Enter)为确定选择 (1)在笔记本电脑关机状态下,使用曲别针或其他物件按下笔记本自带的“一键恢复按钮 ...
- Delphi中的DBGrid控件
在Delphi中,DBGrid控件是一个开发数据库软件不能不使用的控件,其功能非常强大,可以配合SQL语句实现几乎所有数据报表的显示,操作也非常简单,属性.过程.事件等都非常直观,但是使用中,有时侯还 ...
- shell脚本如何获取当前时间
在shell脚本里常常需要获取系统时间来处理某项操作,linux的系统时间在shell里是可以直接调用系统变量的如: 获取今天时期:`date +%Y%m%d` 或 `date +%F` 或 $(da ...
- 51nod 1503 猪和回文(多线程DP)
虚拟两个点,一个从左上角开始走,一个从右下角开始走,定义dp[i][j][k]表示走了i步后,第一个点横向走了j步,第二个点横向走了k步后形成的回文方法种数. 转移方程显然可得,然后滚动数组搞一搞. ...
- 题解 P1423 【小玉在游泳】
这道题可以用简单的蒟蒻do-while循环,方式:直到型 因为是萌新/蒟蒻,所以并不知道这道题的时间/空间复杂度是多大 脚踩std(^▽^)摩擦 #include <iostream> # ...
- NHibernate常见错误
Oracle 下必须用 Sequence [PrimaryKey(PrimaryKeyType.Sequence,"ID")] 1.提示 ORA-02289: 序列不存在 -- C ...
- 【CF331E】Biologist(网络流,最小割)
[CF331E]Biologist(网络流,最小割) 题面 洛谷 翻译: 有一个长度为\(n\)的\(01\)串,将第\(i\)个位置变为另外一个数字的代价是\(v_i\). 有\(m\)个要求 每个 ...
- 康托展开&康托逆展开 的写法
康托展开 康托展开解决的是当前序列在全排序的名次的问题. 例如有五个数字组成的数列:1,2,3,4,5 那么1,2,3,4,5就是全排列的第0个[注意从0开始计数] 1,2,3,5,4就是第1个 1, ...
- excel换行
在excel的单元格中换行 1. windows alt + enter 2. mac command + alt + enter
- ER-18
ER #18简要题解 就是推出循环矩阵乘积 然后一次操作后得到的c矩阵第一行第i列就是i的情况(b矩阵下标是a矩阵下标的转置) 两个循环矩阵乘积还是循环矩阵 以此推式子,发现c矩阵的第一行可以用a,b ...