📝目录
❓︎问题描述
解决0/1背包问题。问题描述:给定n个重量为{w1, w2, … ,wn}、价值为{v1, v2, … ,vn}
的物品和一个容量为C的背包,求这些物品中的一个最有价值的子集,且要能够装到背包中。
🌟方法一:穷举法
核心思想:
穷举法:每件物品装还是不装有两种选择,使用0-表示不装,1表示装,n件物品就有2^n种,穷举2^n种,找到符合符合weight背包容量的且为价值最大的方式。
public class Main01 {
//穷举法
public void pack01(int weight,int[] wt,int[] val){
int n = wt.length;
int count= (int) Math.pow(2,n);
int maxVal = 0;
//枚举32种情况,并且记录符合weight重量背包的最大价值
for (int i = 0; i < count; i++) {
String res = String.format("%5s",Integer.toBinaryString(i)).replace(' ','0');
System.out.print(res+" ");
int sumVal = 0;
int sumWeight=0;
for (int j = 0; j < n; j++) {
//为1时表示装该物品 0表示不准装
if (res.charAt(j)=='1') {
sumVal += val[j];
sumWeight += wt[j];
}
if (sumWeight<=weight){
maxVal = Math.max(sumVal,maxVal);
}
}
System.out.println("价值:"+sumVal+"重量:"+sumWeight);
}
//打印最大价值下对应的背包实际重量和所装物品的状态
for (int i = 0; i<count; i++) {
String res = String.format("%5s",Integer.toBinaryString(i)).replace(' ','0');
int sumVal = 0;
int sumWeight=0;
for (int j = 0; j < n; j++) {
if (res.charAt(j)=='1') {
sumVal += val[j];
sumWeight += wt[j];
}
}
if (sumVal==maxVal&&sumWeight<=weight){
System.out.println("当背包重量为"+weight+"时:最大价值:"+sumVal+" 总重量: "+sumWeight+" 方式:"+res);
break;
}
}
}
public static void main(String[] args) {
Main01 main01 = new Main01();
int[] wt = {1, 2, 1, 12, 4};
int[] val = {1, 2, 2, 4, 10};
main01.pack01(15, wt, val);
}
}
输出结果:
🌟方法二:动态规划
核心思想:
使用二维dp数组:
dp[i][w]数组含义:对于前i个物品,当前背包容量为w时,可装下的最大值是dp[i][w]。
dp[i-1][w-wt[i-1]]+val[i-1]:装物品i的价值
dp[i-1][w]:不装物品i的价值
因此dp[i][w]取装物品 i dp[i-1][w-wt[i-1]]+val[i-1] 和 不装物品i dp[i-1][w] 的最大值
public class Main01 {
public static void main(String[] args) {
int[] wt = {1, 2, 1, 12, 4};
int[] val = {1, 2, 2, 4, 10};
int res = pack01(15,wt,val);
System.out.println("最大价值为:"+res);
}
public static int pack01(int weight,int[] wt,int[] val){
int n = wt.length;
//dp[i][w]数组含义:对于前i个物品,当前背包容量为w时,可装下的最大值是dp[i][w]
int[][] dp = new int[n+1][weight+1];
for (int i = 1; i <= n; i++) {
for (int w = 1; w <= weight; w++) {
if (wt[i-1]>w){
//不能装入背包
dp[i][w] = dp[i-1][w];
}else {
//择优装入背包
dp[i][w] = Math.max(dp[i-1][w-wt[i-1]]+val[i-1],dp[i-1][w]);
}
}
}
//打印dp表
for (int i = 0; i <=n ; i++) {
for (int j = 0; j <=weight ; j++) {
System.out.print(dp[i][j] + "\t");
}
System.out.println();
}
int[] res = new int[n];
// 逆推找出装入背包的所有商品的编号
int j = weight;
String numStr = "";
for (int i = n; i > 0; i--) {
// 若dp[i][j]>dp[i-1][j], 则说明第i件物品放入背包
if (dp[i][j] > dp[i - 1][j]) {
res[i-1] = 1;
numStr = i + "号 " + numStr;
j = j - wt[i-1];
}
if (j <= 0) {
break;
}
}
//编号从1开始
System.out.println("选择物品为: "+numStr);
System.out.println("物品选择状态:"+Arrays.toString(res));
return dp[n][weight];
}
}
输出结果:
🌟方法三:使用回溯法
核心思想:
每件物品选还是不选有两种状态,将选择该物品时,可以作为树的根节点,左子树为选择状态,右子树为不选状态,如下图
import java.util.AbstractMap;
import java.util.LinkedList;
import java.util.List;
public class Pack01backTrack_01 {
int[] wt = {1, 2,1,12,4};
int[] val = {1, 2,2,4,10};
int capacity = 15;
int max_n = wt.length;
boolean[] selected = new boolean[max_n];
int current_weight = 0; // 当前已选物品的总重量
int max_value = 0; // 已找到的最大总价值
List<AbstractMap.SimpleEntry<String,Integer>> list = new LinkedList<>();
StringBuffer res = new StringBuffer();
//搜索到第i个物品
private void backtrack(int i){
if (i==max_n){
if (current_weight<=capacity){
int cur_max = 0;
StringBuffer temp = new StringBuffer();
for (int j = 0; j < max_n; j++) {
if (selected[j]){
cur_max += val[j];
}
if (selected[j]){
temp.append(1+" ");
}else {
temp.append(0+" ");
}
}
list.add(new AbstractMap.SimpleEntry<String,Integer>(temp.toString(),cur_max));
if (max_value<cur_max){
max_value = cur_max;
res = temp;
}
}
return;
}
if (!selected[i]&¤t_weight+wt[i]<=capacity){//物品可以加入背包
selected[i] = true;//选择该物品
current_weight +=wt[i];
backtrack(i+1);//进入下一层
current_weight -= wt[i];//回溯已经选的物品
selected[i] = false;
}
backtrack(i+1);//进入下一层
}
public static void main(String[] args) {
Pack01backTrack_01 pt = new Pack01backTrack_01();
pt.backtrack(0);
System.out.println("最大价值为"+pt.max_value);
System.out.println("选择的物品状态:"+pt.res);
List<AbstractMap.SimpleEntry<String,Integer>> list = pt.list;
System.out.println("所有可行解为:");
for (int i = 0; i < list.size(); i++) {
System.out.println(i+":"+list.get(i).getKey()+":"+list.get(i).getValue());
}
}
}
输出结果: