基本思想:
  动态规划算法通常用于求解具有某种最优性质的问题(作用就是求最优解)。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。

与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。

若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式。

  动态规划算法与分治法最大的差别是:适合于用动态规划法求解的问题,经分解后得到的子问题往往不是互相独立的(即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解)

应用场景:
适用动态规划的问题必须满足最优化原理、无后效性和重叠性。
1.最优化原理(最优子结构性质) 最优化原理可这样阐述:一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。
2.无后效性 将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。
3.子问题的重叠性 动态规划将原来具有指数级时间复杂度的搜索算法改进成了具有多项式时间复杂度的算法。其中的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其它的算法。

动态规划算法经典案例:
案例一:有n级台阶,一个人每次上一级或者两级,问有多少种走完n级台阶的方法。
  分析:动态规划的实现的关键在于能不能准确合理的用动态规划表来抽象出实际问题。在这个问题上,我们让f(n)表示走上n级台阶的方法数。
那么当n为1时,f(n) = 1,n为2时,f(n) =2,就是说当台阶只有一级的时候,方法数是一种,台阶有两级的时候,方法数为2。那么当我们要走上n级台阶,必然是从n-1级台阶迈一步或者是从n-2级台阶迈两步,所以到达n级台阶的方法数必然是到达n-1级台阶的方法数加上到达n-2级台阶的方法数之和。即f(n) = f(n-1)+f(n-2),我们用dp[n]来表示动态规划表,dp[i],i>0,i<=n,表示到达i级台阶的方法数。

import java.util.Scanner;

public class CalculationSteps {
public static long calStep(int n) {
//如果只有1阶,则只有一种,如果2阶,则有两种
if (n == 1 || n == 2) {
return n;
} else {
long[] dp = new long[n];
//dp表的n-1号元素为0的话,那么就向前回一阶
if (dp[n - 1] == 0) {
dp[n - 1] = calStep(n - 1);
}
//dp表的n-号元素为0的话,那么就向前回两阶
if (dp[n - 2] == 0) {
dp[n - 2] = calStep(n - 2);
}
//总的方法就是跳1阶和跳两阶的和
return dp[n - 1] + dp[n - 2];
}
} public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
System.out.println(calStep(n));
}

  

案例2:给定一个矩阵m,从左上角开始每次只能向右走或者向下走,最后达到右下角的位置,路径中所有数字累加起来就是路径和,返回所有路径的最小路径和,如果给定的m如下,那么路径4,1,2,5,2,4,5就是最小路径和,返回23.

4 1 5 3
3 2 7 7
6 5 2 8
8 9 4 5
分析:对于这个题目,假设m是m行n列的矩阵,那么我们用dp[m][n]来抽象这个问题,dp[i][j]表示的是从原点到i,j位置的最短路径和。我们首先计算第一行和第一列,直接累加即可,那么对于其他位置,要么是从它左边的位置达到,要么是从上边的位置达到,我们取左边和上边的较小值,然后加上当前的路径值,就是达到当前点的最短路径。然后从左到右,从上到下依次计算即可。

/**
* 给定一个矩阵m,从左上角开始每次只能向右走或者向下走
* 最后达到右下角的位置,路径中所有数字累加起来就是路径和,
*/
public class ShortestPath {
public static int findShortestPath(int[][] arr) {
int[][] dp = new int[arr.length][arr[0].length];
// 直接设置起始位置
dp[0][0] = arr[0][0];
// 计算第一列dp的值
for (int i = 1; i < arr.length; i++) {
dp[i][0] = dp[i - 1][0] + arr[i][0];
}
// 计算第一行dp的值
for (int i = 1; i < arr[0].length; i++) {
dp[0][i] = dp[0][i - 1] + arr[0][i];
}
// 补全dp规划矩阵
for (int i = 1; i < arr.length; i++) {
for (int j = 1; j < arr[0].length; j++) {
dp[i][j] = arr[i][j] + Math.min(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[arr.length - 1][arr[0].length - 1];
} public static void print(int[][] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
} public static void main(String[] args) {
int[][] arr = { { 4, 1, 5, 3 }, { 3, 2, 7, 7 }, { 6, 5, 2, 8 }, { 8, 9, 4, 5 } };
print(arr);
System.out.println("最短路径长度:" + findShortestPath(arr));
}
}

  

案例3:最长公共子序列问题
  最长公共子序列问题是要找到两个字符串间的最长公共子序列。假设有两个字符串sudjxidjs和xidjxidpolkj,其中djxidj就是他们的最长公共子序列。许多问题都可以看成是公共子序列的变形。例如语音识别问题就可以看成最长公共子序列问题。
  假设两个字符串分别为A=a1a2..am,B=b1b2..bn,则m为A的长度,n为B的长度。那么他们的最长公共子序列分为两种情况。
  1、am=bn,这时他们的公共子序列一定为的长度F(m,n)=F(m-1,n-1)+am;
  2、am≠bn,这时他们的公共子序列一定为的长度F(m,n)=Max(F(m-1,n),F(m,n-1));

public class Lcs {
public static String findLcs(String A, String B) {
//得到A,B的长度,根据长度建立dp表
int n = A.length();
int m = B.length();
//将字符串转换成数组
char[] a = A.toCharArray();
char[] b = B.toCharArray();
String result = "";
int[][] dp = new int[n][m];
//计算第一列dp的值
for (int i = 0; i < n; i++) {
if (a[i] == b[0]) {
dp[i][0] = 1;
result += a[i];
for (int j = i + 1; j < n; j++) {
dp[j][0] = 1;
}
break;
}
}
//计算第一行dp的值
for (int i = 0; i < m; i++) {
if (a[0] == b[i]) {
dp[0][i] = 1;
result += b[i];
for (int j = i + 1; j < m; j++) {
dp[0][j] = 1;
}
break;
}
}
//补全dp的其他位置
for (int i = 1; i < n; i++) {
for (int j = 1; j < m; j++) {
if (a[i] == b[j]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
result += a[i];
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
} return result;
} public static int getLength(String str) {
return str.length();
} public static void main(String[] args) {
String str1 = "bcdhiex";
String str2 = "abcdefg";
String result=findLcs(str1, str2);
System.out.println(result);
int length=getLength(result);
System.out.println("公共子序列长度:"+length);
}
} 

Java算法——动态规划的更多相关文章

  1. 算法-动态规划DP小记

    算法-动态规划DP小记 动态规划算法是一种比较灵活的算法,针对具体的问题要具体分析,其宗旨就是要找出要解决问题的状态,然后逆向转化为求解子问题,最终回到已知的初始态,然后再顺序累计各个子问题的解从而得 ...

  2. java算法 蓝桥杯 摆花

    问题描述 小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共m盆.通过调查顾客的喜好,小明列出了顾客最喜欢的n种花,从1到n标号.为了在门口展出更多种花,规定第i种花不能超过ai盆,摆花时 ...

  3. JAVA算法系列 冒泡排序

    java算法系列之排序 手写冒泡 冒泡算是最基础的一个排序算法,简单的可以理解为,每一趟都拿i与i+1进行比较,两个for循环,时间复杂度为 O(n^2),同时本例与选择排序进行了比较,选择排序又叫直 ...

  4. JAVA算法系列 快速排序

    java算法系列之排序 手写快排 首先说一下什么是快排,比冒泡效率要高,快排的基本思路是首先找到一个基准元素,比如数组中最左边的那个位置,作为基准元素key,之后在最左边和最右边设立两个哨兵,i 和 ...

  5. java算法 蓝桥杯 乘法运算

    问题描述 编制一个乘法运算的程序. 从键盘读入2个100以内的正整数,进行乘法运算并以竖式输出. 输入格式 输入只有一行,是两个用空格隔开的数字,均在1~99之间(含1和99). 输出格式 输出为4行 ...

  6. java算法 蓝桥杯 扶老奶奶街

    一共有5个红领巾,编号分别为A.B.C.D.E,老奶奶被他们其中一个扶过了马路. 五个红领巾各自说话: A :我和E都没有扶老奶奶 B :老奶奶是被C和E其中一个扶过大街的 C :老奶奶是被我和D其中 ...

  7. java算法 蓝桥杯 高精度加法

    问题描述 在C/C++语言中,整型所能表示的范围一般为-231到231(大约21亿),即使long long型,一般也只能表示到-263到263.要想计算更加规模的数,就要用软件来扩展了,比如用数组或 ...

  8. java算法 蓝桥杯 格子位置

    问题描述 输入三个自然数N,i,j (1<=i<=N,1<=j<=N),输出在一个N*N格的棋盘中,与格子(i,j)同行.同列.同一对角线的所有格子的位置. 输入格式 输入共三 ...

  9. 算法-动态规划 Dynamic Programming--从菜鸟到老鸟

    算法-动态规划 Dynamic Programming--从菜鸟到老鸟      版权声明:本文为博主原创文章,转载请标明出处. https://blog.csdn.net/u013309870/ar ...

随机推荐

  1. 让你的GitHub下载飞速提升到2M/s以上

    2020年7月27日整理发布多种GitHub加速方式! 转载自:https://code.pingbook.top/blog/2020/How-To-Speed-Github.html 1. GitH ...

  2. activiti7 导出bpmn文件

    最近在学习springboot+activiti7整合,想做一个导出bpmn文件的功能,查了相关资料,最后没有实现.最后查看了一下代码 找到了方法 如下所示 @GetMapping("exp ...

  3. 【高并发】亿级流量场景下如何为HTTP接口限流?看完我懂了!!

    写在前面 在互联网应用中,高并发系统会面临一个重大的挑战,那就是大量流高并发访问,比如:天猫的双十一.京东618.秒杀.抢购促销等,这些都是典型的大流量高并发场景.关于秒杀,小伙伴们可以参见我的另一篇 ...

  4. 自述:转职IT ,痛苦一阵子;不转职IT,痛苦一辈子(第一章)

    作为一个从后期制作转职过来的Java工程师,我认为我是幸运的,虽然我的本科专业(影视后期)也是火爆行业,不愁工作,但我不后悔进入这个IT坑,毕竟转行,只痛苦一阵子,但是不转行,可能我会痛苦一辈子. 我 ...

  5. Kafka和SpringBoot

    事先必备: kafka已安装完成 1.目录结构 2.父pom <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns ...

  6. 11-14序列化模块之json、pickle、shelve

    序列化的目的 1.以某种存储形式使自定义对象持久化: 2.将对象从一个地方传递到另一个地方. 3.使程序更具维护性. 序列化--转向一个字符串数据类型序列--及时字符串 何处用到: 数据存储 网络上传 ...

  7. Spring事务专题(三)事务的基本概念,Mysql事务处理原理

    前言 本专题大纲: 我重新整理了大纲,思考了很久,决定单独将MySQL的事务实现原理跟Spring中的事务示例分为两篇文章,因为二者毕竟没有什么实际关系,实际上如果你对MySQL的事务原理不感兴趣也可 ...

  8. Hexo小技巧(包括如何插入本地图片)

    我在研究如何在Hexo中引用本地图片时,看到官方文档对此问题已给出了解决方法,并亲测有效.当然,我并不满足于仅仅知道这一个技巧.在大致阅读过官方文档后,我总结了之前我个人并不知道的几个关于Hexo写博 ...

  9. javascript逻辑运算与循环笔记

        短路运算(逻辑中断)     1.短路运算的原理:当有多个表达式(值)时,左边的表达式值可以确定结果的时候就不再继续运算右边的表达式的值     2.逻辑与 &&     如果 ...

  10. VMWare虚拟机问题总结

    windows7提示:在该系统上全局禁用了虚拟打印功能,不会为该虚拟机启用此功能,虚拟设备:'seria10'将断开连接. 解决:打开虚拟机前选择    编辑-->首选项-->设备--&g ...