问题描述:
有n个重量和价值分别为wi,vi的物品,从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中价值总和的最大值。
1≤n≤100
1≤wi,vi≤100
1≤W≤10000
输入:n=4
(w,v)={(2,3),(1,2),(3,4),(2,2)}
W=4
输出:
6(选择第1,2号物品)
思路分析:
对于n个物体来说,就是选与不选的问题,可参照部分和和子集生成的代码。可以画出递归树直观的解释。因为其中重载重叠子问题,故可以使用记忆型递归进行计算,从而加快运行效率。
具体见代码注释:(使用了记忆型递归和非记忆型递归)
import java.util.Arrays;
import java.util.Scanner;
public class Bb01 {
//01背包问题
//有n个重量和价值分别为wi,vi的物品,
// 从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中价值总和的最大值。
private static class bb{//建立重量和价值打包对象
private int w;
private int v;
public bb(int w, int v) {//构造器
this.w = w;
this.v = v;
}
}
public static int solve(bb []bbs,int st,int w)//非记忆型递归
{
if (st==bbs.length) return 0;//递归出口
if (w==0)return 0;
int v1=solve(bbs,st+1,w);//啥都不加的情况的所有和;
if (bbs[st].w<=w)//加入第一个的所有支路之和
{
int v2=solve(bbs,st+1,w-bbs[st].w)+bbs[st].v;
return Math.max(v1,v2);
}
else
return v1;
}
public static int solve2(bb []bbs,int st,int w,int [][]s)//记忆型递归
{
if (st==bbs.length) return 0;//递归出口
if (w==0)return 0;
if (s[st][w]>=0) return s[st][w];//先做查询
int v1=solve(bbs,st+1,w);//啥都不加的情况的所有和;
int ans;
if (bbs[st].w<=w)
{
int v2=solve(bbs,st+1,w-bbs[st].w)+bbs[st].v;
ans=Math.max(v1,v2);
}
else
ans= v1;
s[st][w]=ans;
return ans;
}
public static void main(String[] args) {
Scanner a=new Scanner(System.in);
int n=a.nextInt();//输入对应的物体数量
bb []bbs=new bb[n];//建立对象数组
for (int i = 0; i <n ; i++) {
bbs[i]=new bb(a.nextInt(),a.nextInt());
}
int W=a.nextInt();//输入背包容量
int out=solve(bbs,0,W);//非记忆型递归
int [][]s=new int[n][W+1];//用于记录转台转移量
for (int i = 0; i <n ; i++) {
Arrays.fill(s[i],-1);//二维数组填充为-1
}
int out2=solve2(bbs,0,W,s);//记忆型递归
System.out.println(out);
System.out.println(out2);
}
}
运行结果如下: