动态规划
杭电oj做到了最大子序列和,自己的换了好多方法都超时了,网上一查需要用到动态规划,趁此机会学习一下动态规划
但是感觉动态规划有点像这个递归哈哈哈哈
1.动态规划,就是利用历史记录来避免计算的重复,而这些历史记录我们需要一些变量来保存,一般用到一维数组还有二维数组来保存
2.三个步骤
(1) 定义数组元素的含义,例如你的dp【i】代表的什么含义
(2)找出数组元素之间的关系式,有点类似高中所学的数学归纳法,都是通过已知元素来推未知元素
(3)找出初始值,学过数学归纳法的都知道,虽然我们知道了数组元素之间的关系式,例如 dp[n] = dp[n-1] + dp[n-2],我们可以通过 dp[n-1] 和 dp[n-2] 来计算 dp[n],
但是,我们得知道初始值啊,例如一直推下去的话,会由 dp[3] = dp[2] + dp[1]。而 dp[2] 和 dp[1] 是不能再分解的了,,
所以我们必须要能够直接获得 dp[2] 和 dp[1] 的值而这,就是所谓的初始值。由了初始值,并且有了数组元素之间的关系式
,那么我们就可以得到 dp[n] 的值了,而 dp[n] 的含义是由你来定义的,你想求什么,就定义它是什么,这样,这道题也就解出来了。
3.
小青蛙跳台阶
问题描述,**一只青蛙一次可以跳上一级台阶,也可以跳上二级台阶。求台阶跳上一个n级台阶总共有多少种方法。**
三大步骤
一定义dp的含义,我们的问题是跳上n级台阶时的跳法,我们就定义dp【i】的含义是跳上一个n级台阶总共有dp【i】种跳法。
二.找出元素之间的关系式,因为在每一阶台阶时都是从n-1阶还有n-2阶台阶跳上来的
由于我们要算所有可能的跳法,所以有dp【i】=dp【i-1】+dp【i-2】;
三.找出初始条件
当 n = 1 时,dp[1] = dp[0] + dp[-1],而我们是数组是不允许下标为负数的,所以对于 dp[1],
我们必须要直接给出它的数值,相当于初始值,显然,dp[1] = 1。一样,dp[0] = 0.(因为 0 个台阶,那肯定是 0 种跳法了)。于是得出初始值:
1. dp[0] = 0. dp[1] = 1. | dp[0] = 0. | |-dp[1] = 1.-| | n <= 1
时,dp[n] = n. |
代码 int[] dp = new int[n+1];
// 给出初始值
dp[0] = 0;
dp[1] = 1;
// 通过关系式来计算出 dp[n]
for(int i = 2; i <= n; i++){
dp[i] = dp[i-1] + dp[i-2];
}
(2)
例题二,找零钱
已知不同面值的钞票,求如 何用最少数量的钞票组成某个金额,求可 以使用的最少钞票数量。如果任意数量的已知面值钞票都无法组成该金额, 则返回-1。
输入coins 1 3 5 money 11 输出 3
第一步,确认dp【i】的含义(i元钱时的最少硬币方案)
第二步,找出元素之间的关系式
第三步,找出初始值
dp【0】={0}=0
dp【1】={dp【1-arr【1】】+1}=1
dp【2】={dp【2-arr【1】】+1}=2
dp【3】={dp【3-arr【2】】+1}=1
dp【4】={dp【4-arr【2】】+1}=2
......
**dp【i】={dp【i-arr【j】(arr[j]<i并且是小于i中最大的面值)】+1};**
代码实现:
int money = sc.nextInt();
int[] arr = { 1, 3, 5 };
int[] dp = new int[money + 1];// 每一个都代表一种钱数的拼凑最小值方案
int max = 0;
dp[0] = 0;
for (int i = 1; i < dp.length; i++) {
for (int j = 0; j < arr.length; j++) {
if (arr[j] >= i) {//找到这个临界值,也就是小于i并且是最大的那个面值
if (arr[j] == i) {//如果等于i,证明就是一个硬币就可以
max = j;//通过索引实现这个arr【j】
} else {
max = j - 1;
}
break;//一旦找出这个临界值,立即跳出
}
}
if (max == -1) {
dp[i] = 1;
} else {
dp[i] = dp[i - arr[max]] + 1;
}
}
System.out.println(dp[dp.length - 1]);
}
}
}
```
4.二维数组相关简单例题
(1) 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?例如一个3x7的网格
第一步 确定dp【i】的含义(:当机器人从左上角走到(i, j) 这个位置时,一共有 dp[i] [j] 种路径。那么,dp[m-1] [n-1] 就是我们要的答案了。)
第二步 找出元素之间的关系
第三步 找到初始值
因为每一步只能向右一步或者向下一步,所以有两种方法到达
第一种是从dp【i-1】【j】向下走一步到达
第二种是从dp【i】【j-1】向右走一步到达
又因为dp【i】【j】代表的是到达这一步的路径方法
因为是计算所有可能的步骤,所以是把所有可能走的路径都加起来
所以有dp【i】【j】=dp【i-1】【j】+dp【i】【j-1】
找出初始值,因为当i=0或者j=0时会出现负数情况所以我们的初始值是计算出所有的 dp[0] [0….n-1]
和所有的 dp[0….m-1] [0]。这个还是非常容易计算的,相当于计算机图中的最上面一行和左边一列。因此初始值如下:
dp[0] [0….n-1] = 1; // 相当于最上面一行,机器人只能一直往左走
dp[0…m-1] [0] = 1; // 相当于最左面一列,机器人只能一直往下走
代码实现:
```java
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
int m = sc.nextInt();
int n = sc.nextInt();
int [][]dp = new int[m][n];
for (int i = 0; i < dp.length; i++) {
for (int j = 0; j < dp[0].length; j++) {
if(i==0|j==0) {
dp[i][j]=1;
}
}
}
for (int i = 1; i < dp.length; i++) {
for (int j = 1; j < dp[0].length; j++) {
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
System.out.println(dp[m-1][n-1]);
}
}
```
(2) 例题三
最小路径和问题
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
输入:
**arr=[1,3,1]
[1,5,1]
[4,2,1]**
第一步 定义dp的含义
由于我们的目的是从左上角到右下角,最小路径和是多少,那我们就定义 dp[i] [j]的含义为:。
当机器人从左上角走到(i, j) 这个位置时,最下的路径和是 dp[i] [j]。那么,dp[m-1] [n-1] 就是我们要的答案了
第二步 找出元素之间的关系
到达dp【i】【j】
第一种方法是从dp[i-1][j]向下走一步下来
第二种方法是从dp[i][j-1]向右走一步
选择一种,使得dp[i] [j] 的值是最小的,显然有
dp[i][j]=min{dp[i-1][j]+arr[i][j,dp[i][j-1]+arr[i][j]}
第三步 找出初始值
dp[0] [j] = arr[0] [j] + dp[0] [j-1]; // 相当于最上面一行,机器人只能一直往左走
dp[i] [0] = arr[i] [0] + dp[i] [0]; // 相当于最左面一列,机器人只能一直往下走
代码实现:
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
int m = sc.nextInt();
int n = sc.nextInt();
int [][]arr = new int[m][n];
int [][]dp = new int[m][n];
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[0].length; j++) {
arr[i][j]=sc.nextInt();
}
}
dp[0][0]=arr[0][0];
for (int j = 1; j < dp[0].length; j++) {
dp[0][j]=dp[0][j-1]+arr[0][j];
}
for (int i = 1; i < dp.length; i++) {
dp[i][0]=dp[i-1][0]+arr[i][0];
}
for (int i = 1; i < dp.length; i++) {
for (int j = 1; j < dp[0].length; j++) {
dp[i][j]=Math.min(dp[i-1][j]+arr[i][j], dp[i][j-1]+arr[i][j]);
}
}
System.out.println(dp[m-1][n-1]);
}
}
总结
详解了几道简单的一维数组还有二维数组,陆续将不断更新相关动态规划的题目
来源:CSDN
作者:阳光的羊羊羊羊
链接:https://blog.csdn.net/yjyxw/article/details/103747283