动态规划:基础动态规划讲解?

目录

一、基础题:斐波那契数列

讲解:

斐波那契数列进阶:进击的青蛙

进击的青蛙代码:

斐波那契数列变种:B君的寄望

B君的寄望代码:

二、二维迷宫解:二维的斐波那契数列?

讲解:

真题:过河卒

过河卒代码:

三、迷宫最大值:二维斐波那契数列+贪心?

讲解:

基础真题:数字三角形

数字三角形代码:

基础题进阶:拿金币

拿金币代码:

最新模拟赛题:跳跃

跳跃代码:


一、基础题:斐波那契数列

讲解:

斐波那契数列题目我没找,但是自己搞了一下下:

输入一个数N,让你求该数对应的数x;

这题其实都知道,f(n)=f(n-1)+f(n-2);

且已知前两项为1 1;则可以直接从第三项开始计算,直接一个for循环完事。

斐波那契数列进阶:进击的青蛙

原题地址

初看这题:这和斐波那契数列有啥关系?

但是如果  你把斐波那契数列中的某几个值,让它固定为0,并且每一项为前三项和?然后再把初始已知值变只知道一个值1,不就和斐波那契数列一样了吗?

为什么会有一个已知值1?

因为你一开始的位置是可以往后移动的,就说明你初始所在位置不为0,且你前面没有其他的值了,那么比0大的最小整数就是1;

然后这题还要分类讨论一下:

当你走到第1个格子的时候,你只能从初始位置过去,所以f(1)=f(0);

当你走到第2个格子的时候,你可以从第一个格子+1,也可以从初始值+2;则f(2)=f(1)+f(0);

从第三个格子开始,都是前面三个数的叠加。

注意!!!如果该格子无法走到,无论前面值是多少,该格子始终为0!!! 

进击的青蛙代码:


import java.util.Scanner;
public class 进击的青蛙 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();//有n个数据
        int t=0;
        int k=0;
        long []brr=new long[n+1];
        long a=1,b=0,c=0,d=1;
        brr[0]=1;
        for (int i=1;i<=n;i++){
            t=sc.nextInt();//输入每一个数值
            if (k<3) {//用于计算当前连续的石头个数是否进行运算
                if (t == 1) {//当前位置为石头 则k+1;不然说明连续石头结束,k为0;
                    k++;
                    d=0;//走到当前位置的方法为0,因为不能走石头上
                    if (i>=3) {//只有当个数大于等于三时,才会需要前面的数值更改
                        //以下更改为每个数更改为后一个数 比如 a=1;b=2;c=3;d=0; 更改为 a=2,b=3,c=0;
                        //因为如果下一个数要计算的话 是跳1 2 3 ,则为当前的a b c或者说是更改前的b c d
                        a = b % 1000000007;
                        b = c % 1000000007;
                        c = d % 1000000007;
                    }
                } else {// 当前值为0  说明可以跳到
                    k=0;//清空连续石头个数
                    if (i>=3){//如果 是大于等于三的位置,则为当前位置前三个的方法种数和
                        d=(c+b+a)%1000000007;
                        //求完和后 需要更改位置值,以便下一个值的计算
                        a=b%1000000007;
                        b=c%1000000007;
                        c=d%1000000007;
                    }else if (i==2){
                        //如果是2,则为初始位置和1号位置的种数和
                        c=(a+b)%1000000007;
                    }else{
                        //如果是1,则为初始位置的种数
                        b=(a)%1000000007;
                    }
                }
            }
        }
        if (k>=3||t==1){//说明有连续3个石头,或者最后一个位置是石头,不能跳,输出No Way!
            System.out.println("No Way!");
            return;
        }
        //输出最后的种数
        System.out.println(d);
    }
}

斐波那契数列变种:B君的寄望

原题地址

这题一开始看:诶,不就是斐波那契数列每次+1或者+2,然后再加一个1嘛,只要到n就行了;

那么问题来了:假设长度为3,可以第一次短,第二次短;那能不能第一次长?

很明星,第一次长的话就会直接超了,因为你在停顿的那一秒已经到达了时间要求了;

还有想:那我选择+2后进行一次判断?这也不是不行,但是这样太累了!!!!

这题确实是斐波那契数列,但是难点在于:

将每一个给定值+1!!!!

每次可以+1或者+2,并且停顿1;那么我们看成+2/+3不就行了?那要到最后一个格子刚好是吹的时候,那么,我们只要让最后那个格子的后一个值是+2/+3后的位置不就可以了吗? 

B君的寄望代码:

import java.math.BigInteger;
import java.util.Scanner;
 
public class B君的寄望 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt() + 1;
        if (n==2||n==3||n==4){
            System.out.println(1);
            return;
        }
        BigInteger ai=new BigInteger(String.valueOf(1));
        BigInteger bi=new BigInteger(String.valueOf(1));
        BigInteger ci=new BigInteger(String.valueOf(1));
        BigInteger di=new BigInteger(String.valueOf(1));
        for (int i = 5; i <= n + 1; i++) {
            ci=di;
            di=ai.add(bi);
            ai=bi;
            bi=ci;
        }
        System.out.println(bi);
    }
}

二、二维迷宫解:二维的斐波那契数列?

讲解:

二维迷宫解是什么??二维迷宫解就是:让你从某个位置走到另一个位置有多少种不同的走法,当然还有最短路走法,我们先不考虑最短路怎么走,我们先考虑有多少种不同的走法。

 

 从A到B点,每次只能向下或者向右,有多少种走法?

我们可以发现:B点只能通过5号向下或者7号像右,所以f(b)=f(5)+f(7);

同样的可以求出每一个的数字对应的值;当然,这题我们要设置A点初始值为1,才能找到解。

为什么说这是二维的斐波那契数列呢?

因为它和斐波那契一样:每一个值都是通过前面得到的值获得;

A所在的行和列,都为1:因为它们的上边或者左边不存在数值,所以这两边的数值固定为1;

那么你就可以知道  f(4)=f(1)+f(3)=1+1=2;f(5)=(4)+f(2)=3;......这一行的数值都是前面的数值+1;

那么,f(4)所在的列和这边的数值也是相同的;

真题:过河卒

原题地址

这题就是在上面的讲解中,固定了某几个点为0,走到这个点,无论多大,都归0; 

过河卒代码:

基础版本:


import java.util.Scanner;

public class 过河卒 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int m=sc.nextInt();//n,m表示终点坐标
        int a=sc.nextInt();
        int b=sc.nextInt();//表示马位置坐标
        int [][]arr=new int[n+1][m+1];
        long [][]brr=new long[n+1][m+1];
        arr[a][b]=1;
        if (a-2>=0){
            if (b-1>=0){
                arr[a-2][b-1]=1;
            }
            if (b+1<=n){
                arr[a-2][b+1]=1;
            }
        }
        if (a+2<=m){
            if (b-1>=0){
                arr[a+2][b-1]=1;
            }
            if (b+1<=n){
                arr[a+2][b+1]=1;
            }
        }
        if (b-2>=0){
            if (a-1>=0){
                arr[a-1][b-2]=1;
            }
            if (a+1<=m){
                arr[a+1][b-2]=1;
            }
        }
        if (b+2<=n){
            if (a-1>=0){
                arr[a-1][b+2]=1;
            }
            if (a+1<=m){
                arr[a+1][b+2]=1;
            }
        }
        for (int i=0;i<=n;i++){
            for (int j=0;j<=m;j++){
                if (arr[i][j]==1){
                    brr[i][j]=0;
                }else if (i==0&&j==0){
                    brr[i][j]=1;
                } else if (i == 0) {
                    brr[i][j]=brr[i][j-1];
                }else if (j==0){
                    brr[i][j]=brr[i-1][j];
                }else{
                    brr[i][j]=brr[i-1][j]+brr[i][j-1];
                }
            }
        }
        System.out.println(brr[n][m]);
    }
}

优化版本:

import java.util.Scanner;

public class 过河卒 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt()+2;
        int m=sc.nextInt()+2;//终点位置
        int a=sc.nextInt()+2;
        int b=sc.nextInt()+2;//马的位置
        int [][]arr=new int[n+3][m+3];
        long [][]brr=new long[n+3][m+3];
        arr[a][b]=1;arr[a+2][b+1]=1;arr[a+2][b-1]=1;arr[a-2][b+1]=1;arr[a-2][b-1]=1;
        arr[a-1][b+2]=1;arr[a+1][b+2]=1;arr[a-1][b-2]=1;arr[a+1][b-2]=1;
        brr[1][2]=1;
        for (int i=2;i<n+3;i++){
            for (int j=2;j<m+3;j++){
                if (arr[i][j]==1){
                    brr[i][j]=0;
                }
                else
                brr[i][j]=brr[i-1][j]+brr[i][j-1];
            }
        }
        System.out.println(brr[n][m]);
    }
}

三、迷宫最值:二维迷宫+贪心?

讲解:

这一类题型会让你找出最优解;

而我们知道,贪心算法是局部优解,而dp是全局优解,但是在很多情况下,dp+贪心所得到的答案,为正解!、

迷宫最值就像上面过河卒,将方案数改成最短距离,直接看题型吧:

基础真题:数字三角形

原题地址

从下往上,要求取到的值总和最大,并且每次只能往上或者左上方走

但是这样的思考我个人觉得是有点点困难了,感觉复杂变化和边界值不好掌控;

个人思路:从上往下

因为题目要求到达山顶,那么最上面的值必定获得,然后就可以考虑:在它下面一层值的最大值怎么获得?

走到最上面要最大,那么只能从它的下一层的走到3/8时两个数的最大值中去选择;

这样子的话是从下往上去运算,dp转移公式:arr[a][b]+=Math.max(arr[a+1][b],arr[a+1][b+1]);

那么现在看一下从上往下运算:

 为什么说附带贪心呢?因为在你的每一次选择中,你需要去保存可以得到的最大值

假设:现在你拥有6   你可以获得5个金币  也可以获得3个金币,那么你会选择哪一个?肯定会选择5个金币的吧?但是,这里又不能纯粹的使用贪心,就如图,我们通过答案可以知道,起始位置应该为从5开始往上走,但是如果选择单次最优,那么底部应该为6往上走;

那么怎么来确定当前保留值?

(自下往上的做法讲一下,从上往下的直接看我代码就好)

那么第倒数第二行开始,第一个位置为2,它可以通过最底层的4/5走到,这时候选择4/5中比较大的值,与其本身相加;然后判断7,7可以由5/2经过,选择5,然后后面的两个4,都选择的是6

这样  倒数第二层则为:7  12   10   10;

然后再往上一层用相同的方法:20  13   10;

再往上:23   21 

最顶层就是在23/21中选择最大的那一个!!!

数字三角形代码:

import java.util.*;
 
public class Main {
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
        int n=scanner.nextInt();
        int [][]arr=new int[n][n];
        for(int i=0;i<n;i++){
            for(int j=0;j<=i;j++){
                arr[i][j]=scanner.nextInt();
            }
        }
        int s=arr[0][0];
        for(int i=1;i<n;i++){
            for(int j=0;j<=i;j++){
                if(j==0)
                    arr[i][j]=arr[i-1][j]+arr[i][j];
                else
                    arr[i][j]=Math.max(arr[i-1][j],arr[i-1][j-1])+arr[i][j];
            }
        }
        Arrays.sort(arr[n-1]);
        System.out.println(arr[n-1][n-1]);
	}
}

基础题进阶:拿金币

原题地址

 看完上面的然后看到这个是否发现:好像?和上面的一样一样的?

没错,这题就是将上面的路径数和获取最大金币数结合了起来,不再是一个三角形的取金币;

那么这题的思路就很简单了,在每一种路径的可能下,选择最大金币数的路径,具体思路,从上面讲到这里了,也该自己思考一下了吧?毕竟看一篇文章,总得学会点什么?试试,万一就解出来了呢?代码就在下面,思路也在注释里,卡住了可以看看,不懂也可以问,自己思考一下这个题吧!

拿金币代码:

package com.LQBLX.xunlian;
import java.util.Scanner;
public class 拿金币 {
        public  static  void main(String[] age){
            Scanner sc=new Scanner(System.in);
            int n=sc.nextInt();
            int a[][]=new int[n+1][n+1];//每一个位置的金币数
            for (int i=1;i<=n;i++){//每一行
                for (int j=1;j<=n;j++){//每一列
                    a[i][j]= sc.nextInt();
                }
            }
            for (int i=1;i<=n;i++){
                for (int j=1;j<=n;j++){
                        a[i][j]=a[i][j]+Math.max(a[i-1][j],a[i][j-1]);//加到这个值时,用可以走到这一步的大值去运算
                }
            }
            System.out.println(a[n][n]);
        }
}

最新模拟赛题:跳跃

原题地址

 这一题和上面的拿金币有区别吗?

我个人觉得是没有区别的,不要看它说一次可以走多少格子,你每一次走的时候,还是选择每一次的最优解即可,而每一个值对应的关系式可能会很长(因为它来自于9个位置的最大值!!!)

代码放在下面,看不懂的直接私信我,具体就不多讲解了,因为这属于上面拿金币的一点点加强版,本质上并没有太大区别。

跳跃代码:


import java.io.*;
public class 跳跃 {
    static BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer st=new StreamTokenizer(br);
    public static void main(String[] args) throws Exception{
        int n=nextInt();
        int m=nextInt();
        int [][]arr=new int[n+3][m+3];
        int t=nextInt();
        for (int i=3;i<n+3;i++){
            for (int j=3;j<m+3;j++) {
                if (i == 3 && j == 3) {
                    arr[i][j] = t;
                } else {
                    arr[i][j] = nextInt();
                }
            }
        }
        for (int i=0;i<n+3;i++){
            for (int j=0;j<n+3;j++){
                if (i<3||j<3){
                    arr[i][j]=arr[3][3];
                }
            }
        }
        for (int i=3;i<n+3;i++){
            for (int j=3;j<m+3;j++) {
                if (i == 3 && j == 3) {
                    arr[i][j]=arr[i][j];
                } else {
                    arr[i][j] = arr[i][j] + Math.max(Math.max(Math.max(Math.max(arr[i - 1][j], arr[i - 2][j]), Math.max(arr[i - 3][j], arr[i][j - 1])), Math.max(Math.max(arr[i][j - 2], arr[i][j - 3]), Math.max(arr[i - 1][j - 1], arr[i - 1][j - 2]))), arr[i - 2][j - 1]);
                }
            }
        }
        System.out.println(arr[n+2][m+2]);
    }
    public static int nextInt() throws Exception{
        st.nextToken();
        return (int)st.nval;
    }
}

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值