动态规划

297 阅读3分钟

1.动态规划是什么

动态规划:通过分解问题,将复杂的问题分解为最基本的小问题,找到局部的最优解,然后找到最终问题的最优解的过程。

动态规划关键:

  • 状态转移
    • 字问题之间的转移操作
  • 状态转移方程
    • 描述状态转移的表达式

2.动态规划案例

案例选自 极客时间 程序员的数学基础课

2.1.查询推荐
  • 搜索引擎在搜索单词的时候会有单词的下拉提示和**补全(纠错)**的功能

查询推荐需要注意的点

  • 编辑距离
    • 测量拉丁文的相似度
  • 编辑操作
    • 替换一个字符 (编辑距离加一)
    • 插入一个字符
      • 只有一个增加一个字符 编辑距离+1
      • 两个同事增加一个字符 如果相等编辑距离不变如果不等编辑距离+1
    • 删除一个字符

下边拿两个单词来进行举例

字符串A:kobbe

字符串B: kobe

表格里边记录的是两个字符串的编辑距离

- - -空串B- -k- -o- -b- e
空串A 编辑距离0 (增加一个字符编辑距离+1)1 2 3 4
k 1 min(2,2,0)=0 min(3,1,2)=1 min(4,2,3)=2 min(5,3,4)=3
o 2 min(1,3,2)=1 min(2,2,0) = 0 min(3,1,2)=1 min(4,2,3)=2
b 3 min(2,4,3) = 2 min(1,3,2) = 1 min(2,2,0)=0 min(3,1,2)=1
b 4 min(3,5,4) =3 min(2,4,3)=2 min(1,3,2) =1 min(2,2,1)=1
e 5 min(4,6,5) =4 min(3,5,4)=3 min(2,4,3)=2 min(2,3,1) =1

如图所示最优解就为1

状态转移方程

i == 0 和 j==0 这里就不做赘述。

i>0 和 j>0 的情况。

d[i,j] = min(d[i,j+1] + 1 , d[i+1,j]+1 , d[i,j] + r(i,j) ). {添加的字符相等 r(i,j) = 0,否则r(i,j)=1}

代码如下:

  // 实现状态转移方程
		// 请注意由于 Java 语言实现的关系,代码里的状态转移是从 d[i, j] 到 d[i+1, j+1],而不是从 d[i-1, j-1] 到 d[i, j]。本质上是一样的。
		for (int i = 0; i < a.length(); i++) {
			for (int j = 0; j < b.length(); j++) {
				
				int r = 0;
				if (a.charAt(i) != b.charAt(j)) {
					r = 1;
				} 
				
				int first_append = d[i][j + 1] + 1;
				int second_append = d[i + 1][j] + 1;
				int replace = d[i][j] + r;
				
				int min = Math.min(first_append, second_append);
				min = Math.min(min, replace);
				d[i + 1][j + 1] = min;
				
			}
		}
		
		return d[a.length()][b.length()];
				
	}

}

2.2 钱币组合的最优解

假设有 三种币种 2,5,7 求这三种币种组成 100 元 用的最小的纸币张数。

方程如下:

c[totalMoney] = c[totalMoney - moneyKind()] + 1;

代码如下:

private static int totalNumberForMoney(int[] moneyKind,int total){
        //初始化 c数组
        int[] c = new int[total+1];
        for(int i=1;i<c.length;i++){
            c[i] = -1;
        }
        c[0] = 0;
        for(int i=1;i<=total;i++){
             int[] data = new int[moneyKind.length];
              for(int j = 0;j<moneyKind.length;j++){
                  if((i - moneyKind[j])<0){
                      data[j]= -1;
                      continue;
                  }
                  data[j] = c[i - moneyKind[j]];
              }
              int min = min(data);
              if(min == -1){
                  continue;
              }
              c[i] = min + 1;
            System.out.println(i + "min" + c[i]);
        }
        return c[total];
    }

    private static int min(int[] data) {
        boolean flag = true;
        int min = -1;
        for(int i=0;i<data.length;i++){
            if(data[i]==-1){
                continue;
            }
            if(flag){
                min = data[i];
                flag = false;
                continue;
            }
            if(data[i]<min){
                min = data[i];
            }
        }
        return min;
    }

3.总结

动态规划的适用场景:

**最小值,最大值,最优解,最段子串,最长子串.....**等等

解决动态规划问题的关键就是写出 问题转移的方程,然后转换成程序。

而且动态规划的时间复杂度 为mn 如果相同的问题使用递归循环(排列组合)进行解的话,考虑的各种复杂的情况需要穷举出所有的可能随着数据量的增加时间复杂度要比mn大的多。