一、动态规划
代表一类问题(最优子结构或子问题最优性)的一般解法,是设计方法或者策略,不是具体算法
本质:递推,核心是找到状态转移的方式,写出dp方程。
解决问题:交叉,重叠子问题(最优子问题)
形式:
记忆型递归
递推
与深搜的区别:深搜要所有符合条件的解,动态规划要求最优解(唯一解)
二、题目
有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,3)}
w=5
输出
7(选择第0,1,3号物品)
分析思路
- 方法一:递推
因为对每个物品只有选和不选两种情况,所以这个问题称为01背包问题。
两个决定性量,重量,价值,
物品只有两种选择拿或者不拿,在重量限定的条件下,使得价值达到最大
与深搜的区别,深搜要所有符合条件的解,动态规划要求最优解
用深搜进行分析,找一个卡点,找到之后只能从卡点开始往后选
- 代码:
public class Bag01 {
static int[] w= {2,1,3,2}; //重量表
static int[] v= {3,2,4,2}; //价值表
static int n=4; //物品数量
static int W=5; //背包的承重极限
static int max(int a,int b) {
return a;
}
public static void main(String[] args) {
int ww=W;
int ans=dfs(0,ww); //从第一个物品开始
System.out.println(ans);
}
static int dfs(int i,int ww) {
//出口条件
if(ww<=0) return 0; //装不进去
if(i==n) return 0; //没东西可选了
int v2=dfs(i+1,ww); //不选择当前物品
if(ww>=w[i]) {
int v1=v[i]+dfs(i+1,ww-w[i]); //选择当前物品
return max(v1,v2);
}
else {
return v2;
}
}
}
- 方法二:记忆型递归(处理重叠子问题)
二叉树:递归自下往上推
括号前缀代表层数和从第几个物品开始选,开始选的编号越小所选的范围越大
记忆:把f(3,2)这样重叠的问题记忆下来
f(x,y)采用二维数组
x:长度n,物品数量[0,n-1]
y:总重量 开辟到w+1
二维数组第n行第m列
把二维数组都填为-1
记忆递归:
1.计算前查询
2.计算后保存
- 代码
import java.util.Arrays;
public class Bag01jiyi {
static int[] w= {2,1,3,2}; //重量表
static int[] v= {3,2,4,2}; //价值表
static int n=4; //物品数量
static int W=5; //背包的承重极限
static int max(int a,int b) {
return a;
}
public static void main(String[] args) {
int ww=W;
int ans=m(0,ww); //从第一个物品开始
System.out.println(ans);
rec=new int[n][W+1]; //二维数组存放
for(int i=0;i<n;i++) {
Arrays.fill(rec[i], -1); //重量最小为0
}
ww=W;
ans=m(0,ww);
System.out.println(ans);
}
static int[][] rec;
static int m(int i,int ww) {
//出口条件
if(ww<=0) return 0;
if(i==n) return 0;
//就算之前先查询
if(rec[i][ww]>=0)
return rec[i][ww];
int v2=m(i+1,ww); //不选择当前物品
int ans;
if(ww>=w[i]) {
int v1=v[i]+m(i+1,ww-w[i]); //选择当前物品
ans=max(v1,v2);
}
else {
ans=v2;
}
//计算之后做保存
rec[i][ww]=ans;
return ans;
}
}