动态规划笔记
一、基本思想
- 是将带求解的问题分解成若干个子问题,先求子问题,然后子问题的解得到原问题的答案
- 与分治法的不同的是 : 适用于动态规划求解的问题,经分解得到的问题往往不是互相独立的
- 动态规划算法适用于解最优化的问题:
- 找出最优解的性质,并刻画其结构特征
- 递归定义最优值
- 以自底向上的方式计算出最优值
- 而 使用动态规划求解最优解问题,必须满足,最优解的每个局部解也都是最优的
- 每个动态规划的问题都是从一个网格开始的
二、0-1背包问题
问题: 背包 大小为 4,,,有三件物品 价值和重量,物品2(3000,4)物品3(2000,3)物品1(1500,1),计算最大价值
1. 首先了解基本思想:
- 要想解决大背包的问题,
- 那么先解决小背包的问题
- 建立网格
- 了解执行过程,得出每个单元格的计算公式
2. 网格和执行过程
- 首先计算 物品1 放入背包,产生的最大价值
- 并且假定只能放入物品1 可以选择
物品不同容量的背包 | 1 | 2 | 3 | 4 |
---|---|---|---|---|
物品1 | 1500 | 1500 | 1500 | 1500 |
物品2 | ||||
物品3 |
原来的问题是要求容量为4的背包,为什么还有算 1,2,3 容量的问题 : 动态规划,就是从小问题来推导出大的问题的答案,而背包问题的小问题,就是小背包
则由上面表格得出: 应该选择1500 的物品1 ,是最大价值
- 然后计算第二行,放入背包的可以有物品1,和物品2 。
物品不同容量的背包 | 1 | 2 | 3 | 4 |
---|---|---|---|---|
物品1 | 1500 | 1500 | 1500 | 1500 |
物品2 | 1500 | 1500 | 1500 | 3000 |
物品3 |
- 因为物品2 是 3 大小,故 前面 的小背包还是放入 物品1 产生最大值,而在 容量 4 的背包的最大价值为 3000,为最新的最大价值
- 三个物品都能够放入
物品不同容量的背包 1 2 3 4 物品1 1500 1500 1500 1500 物品2 1500 1500 1500 3000 物品3 1500 1500 2000 3500
可以得出 最终答案为 3500 为最大价值
但是有个问题: : 这里 0-1背包问题的最优解 : 是在 表格最后这里,但是不代表每一个动态规划的算法,最优答案都是在这个,。
3. 得出单元格的计算公式
- 上一个单元格的值(即cell[i-1][j]的值
cell[i][j] = 两个中较大的那个 VS
- 当前商品的价值 + 剩余空间的价值 (cell[i-1][j - 当前商品的重量]
4.贴出书中的答案:仅供参考
int knapSack(int n ,int w[] , int v[]){
for(int i= 0; i<= n; i++){
V[i][0] = 0;
}
for(j = 0; j<= C; j++){
V[0][j] = 0;
}
for (i=1; i<=n; i++) //计算第i行,进行第i次迭代
for (j=1; j<=C; j++)
if (j<w[i]) V[i][j]=V[i-1][j];
else V[i][j]=max(V[i-1][j], V[i-1][j-w[i]]+v[i]);
j=C; //求装入背包的物品
for (i=n; i>0; i--){
if (V[i][j]>V[i-1][j]) {
x[i]=1;
j=j-w[i];
}
else x[i]=0;
}
return V[n][C]; //返回背包取得的最大价值
}
5. 如果不至是三个物品,还有一个0.5大小的物品
那么你需要考虑的粒度更新,因此必须调整网格
例如:
背包大小 分为 : 0.5 , 1 , 1.5 , 2 , 2.5 , 3 , 3.5 , 4
三、编辑距离问题
问题描述: 设A和B是2 个字符串,要用最少的字符操作将字符串A转换为字符串b,,计算他们的编辑距离
fxpimu
xwrs
输出 5
1. 网格和执行过程
而单元格 现在代表的是,第一个字符串的前i个字母 和第二字符串 的前j个字母需要编辑的次数
1 | 2 | 3 | 4 | |
---|---|---|---|---|
1 | 1 | 2 | 3 | 4 |
2 | 1 | 2 | 3 | 4 |
3 | 2 | 2 | 3 | 4 |
4 | 3 | 3 | 3 | 4 |
5 | 4 | 4 | 4 | 4 |
6 | 5 | 5 | 5 | 5 |
2. 得出单元格的计算公式
if (n.charAt(i - 1) == m.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
System.out.println(i+"-"+j+"->" + dp[i][j]);
}else {
dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i - 1][j], dp[i][j - 1])) + 1;
System.out.println(i+"-"+j+" ->" + dp[i][j]);
}
3. 参考算法
package book.practices.code;
/**
* @version : 1.0
* @auther : Firewine
* @Program Name: code32
* @Create : 2019/12/28
* @Description :
*/
public class code32 {
/**
* 编辑距离问题
* fxpimu
* xwrs
*
*
*
* 输出5
*
*
* 状态定义:Fi,j表示第一个字符串的前i个字母和第二个字符串的前j个字母需要编辑的次数,
* 求Fn,m,n和m分别是两个字符串的长度。
*
* 状态转移方程:
* 当Fi,j-1=Fi-1,j时,Fi,j=Fi,j-1;
* 当Fi,j-1!=Fi-1,j时,Fi,j=min{Fi-1,j-1,Fi,j-1,Fi-1,j}+1.
*/
private void solve (String n,String m){
int aLen = n.length();
int bLen = m.length();
int[][] dp = new int[aLen+1][bLen+1];
for (int i= 0; i< aLen+1;i++){
dp[i][0]=i;
}
for (int i = 0;i < bLen+1;i++){
dp[0][i] = i;
}
//开始状态运算
for (int i = 1; i < aLen+1; i++) {
for (int j = 1; j<bLen+1;j++){
if (n.charAt(i - 1) == m.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
System.out.println(i+"-"+j+"->" + dp[i][j]);
}else {
dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i - 1][j], dp[i][j - 1])) + 1;
System.out.println(i+"-"+j+" ->" + dp[i][j]);
}
}
}
System.out.println(dp[aLen][bLen]);
}
public static void main(String[] args) {
code32 demo = new code32();
String n = "fxpimu";
String m = "xwrs";
demo.solve(n,m);
}
}