Java算法——动态规划
基本思想:
动态规划算法通常用于求解具有某种最优性质的问题(作用就是求最优解)。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。
若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式。
动态规划算法与分治法最大的差别是:适合于用动态规划法求解的问题,经分解后得到的子问题往往不是互相独立的(即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解)
应用场景:
适用动态规划的问题必须满足最优化原理、无后效性和重叠性。
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算法——动态规划的更多相关文章
- 算法-动态规划DP小记
算法-动态规划DP小记 动态规划算法是一种比较灵活的算法,针对具体的问题要具体分析,其宗旨就是要找出要解决问题的状态,然后逆向转化为求解子问题,最终回到已知的初始态,然后再顺序累计各个子问题的解从而得 ...
- java算法 蓝桥杯 摆花
问题描述 小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共m盆.通过调查顾客的喜好,小明列出了顾客最喜欢的n种花,从1到n标号.为了在门口展出更多种花,规定第i种花不能超过ai盆,摆花时 ...
- JAVA算法系列 冒泡排序
java算法系列之排序 手写冒泡 冒泡算是最基础的一个排序算法,简单的可以理解为,每一趟都拿i与i+1进行比较,两个for循环,时间复杂度为 O(n^2),同时本例与选择排序进行了比较,选择排序又叫直 ...
- JAVA算法系列 快速排序
java算法系列之排序 手写快排 首先说一下什么是快排,比冒泡效率要高,快排的基本思路是首先找到一个基准元素,比如数组中最左边的那个位置,作为基准元素key,之后在最左边和最右边设立两个哨兵,i 和 ...
- java算法 蓝桥杯 乘法运算
问题描述 编制一个乘法运算的程序. 从键盘读入2个100以内的正整数,进行乘法运算并以竖式输出. 输入格式 输入只有一行,是两个用空格隔开的数字,均在1~99之间(含1和99). 输出格式 输出为4行 ...
- java算法 蓝桥杯 扶老奶奶街
一共有5个红领巾,编号分别为A.B.C.D.E,老奶奶被他们其中一个扶过了马路. 五个红领巾各自说话: A :我和E都没有扶老奶奶 B :老奶奶是被C和E其中一个扶过大街的 C :老奶奶是被我和D其中 ...
- java算法 蓝桥杯 高精度加法
问题描述 在C/C++语言中,整型所能表示的范围一般为-231到231(大约21亿),即使long long型,一般也只能表示到-263到263.要想计算更加规模的数,就要用软件来扩展了,比如用数组或 ...
- java算法 蓝桥杯 格子位置
问题描述 输入三个自然数N,i,j (1<=i<=N,1<=j<=N),输出在一个N*N格的棋盘中,与格子(i,j)同行.同列.同一对角线的所有格子的位置. 输入格式 输入共三 ...
- 算法-动态规划 Dynamic Programming--从菜鸟到老鸟
算法-动态规划 Dynamic Programming--从菜鸟到老鸟 版权声明:本文为博主原创文章,转载请标明出处. https://blog.csdn.net/u013309870/ar ...
随机推荐
- 油田问题 bfs
#include<iostream> #include<stdio.h> #include<stdlib.h> #include<time.h> #in ...
- Day17_购物车
学于黑马和传智播客联合做的教学项目 感谢 黑马官网 传智播客官网 微信搜索"艺术行者",关注并回复关键词"乐优商城"获取视频和教程资料! b站在线视频 注:实验 ...
- Python 数字数据类型
数字数据类型,包括整数.浮点数.复数和布尔类型. 整数 int 长整型(数字长度不限制):包括正数,负数,0. # 正数 num_int = 123 print(type(num_int)) # &l ...
- PHP getdate() 函数
------------恢复内容开始------------ 实例 返回当前本地的日期/时间的日期/时间信息: <?phpprint_r(getdate());?> 运行实例 » 定义和用 ...
- HTML <html> 标签
HTML <html> 标签 实例 简单的 HTML5 文档: <!DOCTYPE html><html>高佣联盟 www.cgewang.com<head& ...
- PHP is_null() 函数
is_null() 函数用于检测变量是否为 NULL.高佣联盟 www.cgewang.com PHP 版本要求: PHP 4 >= 4.0.4, PHP 5, PHP 7 语法 bool is ...
- PHP libxml_clear_errors() 函数
定义和用法 libxml_clear_errors() 函数清空 libxml 错误缓冲. 语法 libxml_clear_errors() 实例 <?phplibxml_clear_error ...
- luogu P5410 模板 扩展 KMP Z函数 模板
LINK:P5410 模板 扩展 KMP Z 函数 画了10min学习了一下. 不算很难 思想就是利用前面的最长匹配来更新后面的东西. 复杂度是线性的 如果不要求线性可能直接上SA更舒服一点? 不管了 ...
- E CF R 85 div2 1334E. Divisor Paths
LINK:Divisor Paths 考试的时候已经想到结论了 可是质因数分解想法错了 导致自闭. 一张图 一共有D个节点 每个节点x会向y连边 当且仅当y|x,x/y是一个质数. 设f(d)表示d的 ...
- Jenkins总结3-shell脚本
我写shell脚本的功力还很初级,基本都是现学现卖,写得不是很健壮,只能提供个思路,请大家包涵. 我使用的系统只能发函数放到shell最前面.本人还是比较推崇函数式脚本的,方便复用,目前只简单的封装了 ...