最近学到动态规划,把写的代码题分享一下。
动态规划顾名思义,就是在处理任务的时候,任务的脉络不是预先被完全预料好的,需要在运算中
一步步推算的。
这么说可能很难理解它的特殊性我来举几个例子。
最著名的就是我们的青蛙跳台阶问题。(青蛙一次跳一或两阶,跳n阶有多少种跳法)
有的同学可能就开始混淆了,明明这个是经典的递归问题,类似斐波那契数列,怎么会扯上动态规划呢?
我们先看看一般解法:(递归)
逻辑大概是这样的,青蛙在跳第到n阶的时候,它可能是从n-1阶跳一阶上来的,也有可能是从n-2阶跳上来的。这样就将问题简单化。
接着进行递归,结束条件是n=1/2(负数几乎不可能)
代码如下:
public class FrogJump {
public static int jumpWays(int n) {
if (n <= 0) {
return 0;
}
if (n == 1) {
return 1;
}
if (n == 2) {
return 2;
}
// 当前台阶可以选择跳1级或者2级
return jumpWays(n - 1) + jumpWays(n - 2);
}
public static void main(String[] args) {
int n = 5; // 台阶数
int ways = jumpWays(n);
System.out.println("跳上 " + n + " 级台阶的方法数为:" + ways);
}
}
那么动态规划如何解这个递归问题呢?
很有意思吧,结构出奇的一致,却有些不同
public class FrogJump {
public static int jumpWaysDP(int n) {
if (n <= 0) {
return 0;
}
if (n == 1) {
return 1;
}
if (n == 2) {
return 2;
}
int[] dp = new int[n+1];
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
public static void main(String[] args) {
int n = 5; // 台阶数
int ways = jumpWaysDP(n);
System.out.println("跳上 " + n + " 级台阶的方法数为:" + ways);
}
}
你看这个代码是将每一阶的跳法存储在一个数组中,然后再对数组的数进行处理,这里的逻辑是相加。这就成了一个动态规划的代码。
接着我们来看问题:
问题很有意思,有顺序的一组数(并没有经过排序)给到你,你不可以选相邻的,最后选出满足和最大的所有数。(只需要和)
这个问题复杂起来了,因为0并不是选取最大的数就行,你得考虑到你前面选定的数可能在一个特殊的时候需要推倒从来,我给你一个例子。
给你一个例子。
1,2,3,4,1000,5这个数组。
你从前往后看,1和2比较明显2大,因此我选择2;
选择2后往后走发现出现比2更大的3,因此选择3.现在我们不能忘记1,它也可以被装进来。
接下来是4,如果选择4的话明显4+2>1+3,因此抛弃1,3选择2,4.
接着是1000这个数,1000+1+3>2+4+5。因此选择1,3,1000.
接着到5,1+3+1000>2+4+5.
结果是什么1+3+1000.
我们来看这个计算的过程,发现明显是怎么样的?
明显是冗杂的,动态的,符合我们之前讲的动态规划,我们来应用一下。
利用数组来存储每一步选择后的情况,比如说1+3,2+4明显应用了多次,我们大可以用数组进行储存,后面来了新数进行比较。
怎么比较呢,例如来了个新数字x,和挨着的上次的和进行比较。多说无益,我们来看代码
import java.util.Scanner;
public class oj1 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int[] values = new int[n]; // 例子数组
for (int i = 0; i < n; i++) {
values[i] = scanner.nextInt();
}
int[] dp = new int[n]; // 创建一个数组用于存储最大值
// 初始化动态规划数组
dp[0] = values[0];
if (n > 1) {
dp[1] = Math.max(values[0], values[1]);
}
// 动态规划求解
for (int i = 2; i < n; i++) {
dp[i] = Math.max(dp[i - 1], dp[i - 2] + values[i]);
}
// 打印最大总值
System.out.println( dp[n - 1]);
scanner.close(); // 关闭 Scanner 对象
}
}
你要知道下一个最大和就是max(前一个最大和,前前一次最大和+当前值)
很有意思吧,这么简单的代码循环起来就不一样,多去试试,这里的关键就是你之前求得最大和并不会改变了。