动态规划-背包问题
1、0/1背包问题:在N件物品取出若干件放在容量为W的背包里,每件物品的体积为W1,W2……Wn(Wi为整数),与之相对应的价值为P1,P2……Pn(Pi为整数)。求背包能够容纳的最大价值。
递推公式:F(i, j) = Max{ F(i-1, j)+ F(i-1, j-wi)+pi }
原理分析:考虑第i件物品,假设不加入,最大值则为放入i-1件时的最大值;假设加入,最大值则为当前值+剩余空间的最大值,最终两者情况相比取其大。子问题也遵循相同的原理。
2、代码实现:
(1)递归实现:
import java.util.Scanner;
/**
*
* @author
* 0/1背包问题
*/
public class Main9 {
public static void main(String args[]) {
Scanner s = new Scanner(System.in);
/**
* 定义测试数据
*/
int w[] = {1,5,2,3,4};
int v[] = {5,7,2,6,100};
int len = w.length;
int topW = 8;
int topV = findMaxVal(w,v,len-1,topW);
System.out.println(topV);
s.close();
}
public static int findMaxVal(int[] w,int[] v,int cur_index,int cur_topw) {
/*
* 递归结束条件:没有物品可取
*/
if(cur_index<0) {
return 0;
}
//假设不取第i件物品
int lastMax = findMaxVal(w, v,cur_index-1, cur_topw);
int curMax = lastMax;
if(w[cur_index]<=cur_topw) {
//假设取第i件物品
curMax = Math.max(lastMax, findMaxVal(w,v,cur_index-1,(cur_topw-w[cur_index]))+v[cur_index]);
}
return curMax;
}
}
(2)递归遍历+记忆搜索:当第一次计算时记录值。缩小遍历次数。
import java.util.Scanner;
public class Main9_2 {
public static void main(String args[]) {
Scanner s = new Scanner(System.in);
/**
* 定义测试数据
*/
int w[] = {1,5,2,3,4};
int v[] = {5,7,2,6,1};
int len = w.length;
int topW = 8;
int keep[][] = new int[len][topW+1];
//先将记录数组初始化为-1.
for(int i=0;i<len;i++) {
for(int j=0;j<=topW;j++) {
keep[i][j] = -1;
}
}
int topV = findMaxVal(w,v,len-1,topW,keep);
System.out.println(topV);
s.close();
}
public static int findMaxVal(int[] w,int[] v,int cur_index,int cur_topw,int keep[][]) {
/*
* 递归结束条件:选择到最后一件物品
*/
if(cur_index==0) {
int res = w[0]<=cur_topw?v[0]:0;
return res;
}
//假设不取第i件物品,查询子问题是否被记录
int lastMax= keep[cur_index-1][cur_topw]==-1?
findMaxVal(w, v,cur_index-1, cur_topw,keep):keep[cur_index-1][cur_topw];
int curMax = lastMax;
if(w[cur_index]<=cur_topw) {
//假设取第i件物品,递归之前查询是否被记录。
int temp = keep[cur_index-1][cur_topw-w[cur_index]]==-1?findMaxVal(w,v,cur_index-1,(cur_topw-w[cur_index]),keep):
keep[cur_index-1][cur_topw-w[cur_index]];
curMax = Math.max(lastMax,temp+v[cur_index]);
}
return curMax;
}
}
(3)多重循环实现:使用二维数组记录结果
import java.util.Scanner;
public class Main9_3 {
public static void main(String args[]) {
Scanner s = new Scanner(System.in);
//定义测试数据
int w[] = {1,5,2,3,4};
int v[] = {5,7,2,6,1};
int len = w.length;
int topW = 8;
int keep[][] = new int[len][topW+1];
for(int i=0;i<=topW;i++) {
keep[0][i] = w[0]<=i?v[0]:0;
}
//开始填表(二维数组)
for(int i=1;i<len;i++) {
for(int j=0;j<=topW;j++) {
keep[i][j] = keep[i-1][j];
if(w[i]<=j) {
keep[i][j] = Math.max(keep[i][j], keep[i-1][j-w[i]]+v[i]);
}
}
}
//打印填表结果,实际可省略
for(int i=0;i<len;i++) {
for(int j=0;j<=topW;j++) {
System.out.print(keep[i][j]+" ");
}
System.out.println();
}
System.out.println(keep[len-1][topW]);
s.close();
}
}
(4)多重循环实现:使用一维循环数组记录当前结果。
》要点:记录当前值时,需要从后往前更新(这也是循环的体现)。
package yrc1;
import java.util.Scanner;
public class Main9_4 {
public static void main(String args[]) {
//最后结果:0 5 5 7 11 11 13 13 14
Scanner s = new Scanner(System.in);
//定义测试数据
int w[] = {1,5,2,3,4};
int v[] = {5,7,2,6,1};
int len = w.length;
int topW = 8;
int keep[] = new int[topW+1];
for(int i=0;i<=topW;i++) {
keep[i] = w[0]<=i?v[0]:0;
}
for(int i=1;i<len;i++) {
for(int j=topW;j>=w[i];j--) {
keep[j] = Math.max(keep[j], keep[j-w[i]]+v[i]);
}
}
//显示最后记录结果
for(int i=0;i<=topW;i++) {
System.out.print(keep[i]+" ");
}
System.out.println("\n"+keep[topW]);
}
}

浙公网安备 33010602011771号