动态规划:
- 最优子问题 或者 子问题最优性的一般算法
- 找出dp方程
- 解决重叠子问题(减少求解次数)是记忆型递推、递归
1.递归解决
个人觉得这种方法更加容易理解
重点是将无脑的递归转化为,有记忆形式的递归。(也就是去掉重复计算)
也就是利用空间交换时间上的效率。
import java.util.Scanner;
public class Main {
public static int dp[][];
public static int v[],w[];
public static int n,m;
public static void main(String[] args) {
Scanner sca = new Scanner(System.in);
n = sca.nextInt(); //物品个数
m = sca.nextInt(); //背包容量
v = new int[n + 1];
w = new int[n + 1];
dp = new int[n + 1][m + 1];
for(int i = 1 ; i < n + 1; i ++) {
w[i] = sca.nextInt();
v[i] = sca.nextInt();
}
//初始化,相当于标记为没有记忆
for(int i = 1 ;i <= n; i++) {
for(int j = 1; j <= m ; j++) {
dp[i][j] = -1;
}
}
int res = dfs(m, 1);
System.out.println(res);
}
private static int dfs(int m2, int i) {
int res = 0;
if(i == n + 1) {
return 0;
}
if(m2 < 0) {
return 0;
}
//1.计算之前先判断
if(dp[i][m2] > 0) {
return dp[m2][i];
}
int x1 = dfs(m2, i + 1); //不放
if(m2 - v[i] >= 0) {
int x2 = w[i] + dfs(m2 - v[i], i + 1);//比较放与不放的哪个好
res = max(x1, x2);
}
else {
res = x1;
}
//2.计算之后做记忆
dp[i][m2] = res;
return res;
}
private static int max(int i, int j) {
if(i >= j) {
return i;
}
return j;
}
}
运行结果1:
5
10
2 1
3 5
2 5
3 4
4 3
9
2.递推解决 (近一步优化)
import java.util.Scanner;
public class Main {
public static int dp[][];
public static int v[],w[];
public static int n,m;
public static void main(String[] args) {
Scanner sca = new Scanner(System.in);
n = sca.nextInt();
m = sca.nextInt();
v = new int[n +1];
w = new int[n+1];
dp = new int[n+1][m+1];
for(int i = 1 ; i < n+1; i ++) {
w[i] = sca.nextInt();
v[i] = sca.nextInt();
}
for(int i = 1 ;i <= n; i++) {
for(int j = 1; j <= m ; j++) {
if( v[i] <= j) {
dp[i][j] = max(dp[i-1][j - v[i]] + w[i],dp[i-1][j] );
}
else {
dp[i][j] = dp[i-1][j];
}
}
}
System.out.println(dp[n][m]);
}
private static int max(int i, int j) {
if(i >= j) {
return i;
}
return j;
}
}
运行结果1:
3
2
2 3
3 3
2 1
2
运行结果2:
5
10
2 1
3 5
2 5
3 4
4 3
9
3.空间优化
由于下一行要计算的是在i- 1行的基础上计算,所以从后向前面进行计算,依次覆盖前面。
import java.util.Scanner;
public class Main {
public static int m,n;
public static int dp[],w[],v[];
public static void main(String[] args) {
Scanner sca = new Scanner(System.in);
n = sca.nextInt();
m = sca.nextInt();
dp = new int[m+1];
v = new int[n+1];
w = new int[n+1];
for(int i = 1; i <= n;i ++) {
w[i] = sca.nextInt();
v[i] = sca.nextInt();
}
for(int i = 1; i < n+1; i++) {
for(int j = m;j >= v[i];j --) {
if(dp[j] < dp[j - v[i]] + w[i])
dp[j] = dp[j - v[i]] + w[i];
}
}
System.out.println(dp[m]);
}
}
运行结果与上述一样。
它的全部优化过程就类似于斐波拉契解法的全部优化过程一样:
- 直接用dfs求,时间复杂度为O(
),时间较长
- dfs优化,转化为记忆型的递归(动态规划),当数据时间大大比原始减少
- 直接用二维的数组,递推求出f(n, i) = f(n - 1 , i -1) - f(n -2 , i -1)
- 空间优化直接用一维数组存放,并且反复覆盖f(n) = f(n - 1) - f(n -2)