每天一道动态规划——第一天

动态规划一定要去尝试!

题目一:

1)题目描述

一共有N个位置,机器人从当前位置cur走到目标位置aim,有res步可以走,问一共有多少种方法。
题目示例:

1 2 3 4 5 6
N=6|cur=2|aim=3|res=3
结果:1
分析:2-3-4-3

2)代码

public class demo01 {

    public static void main(String[] args) {
        int res = 13;
        int cur = 2;
        int aim = 3;
        int N = 7;

        int result = 0;

        result = process1(cur, aim, res, N);
        System.out.println(result);
        
        int[][] dp = new int[N + 1][res + 1];
        for (int i = 0; i <= N; i++) {
            for (int j = 0; j <= res; j++) {
                dp[i][j] = -1;
            }
        }
        int result2=process2(cur, aim, res, N,dp);
        System.out.println(result2);
    }

    public static int process1(int cur, int aim, int res, int N) {
        /*
        cur:当前位置
        aim:目标位置
        res:剩余步数
        N:共有哪些位置
         */
        if (res == 0) {
            return cur == aim ? 1 : 0;
        }
        if (cur == 1) {
            return process1(2, aim, res - 1, N);
        } else if (cur == N) {
            return process1(N - 1, aim, res - 1, N);
        } else {
            return process1(cur - 1, aim, res - 1, N) + process1(cur + 1, aim, res - 1, N);
        }

    }
    }

3)代码优化

当暴力递归使总是出现重复解,可以通过空间换时间的方法来进行优化。

但当一个暴力递归中不存在重复解,那么它本身就是不可以优化的。

我们总说动态规划的dp表,我觉得最好的理解就是缓存表。

我们可以发现,结果是由cur与res唯一决定的(此时我认为是由状态唯一决定的)。

如果你之前计算过cur还剩res步的时候,此时又要计算,那为什么不用一个二元组把数据记录下来呢?记为dp[cur][res]。

cur的范围为:1~N
res的范围为:0~K
所以对二元状态表进行初始化:
-1为没计算过;
!=-1为计算过;—有缓存先走缓存

优化代码如下:

package com.sq.demo72DynamicPlan;

public class demo01 {
    static int res = 3;
    static int cur = 2;
    static int aim = 3;
    static int N = 7;

    public static void main(String[] args) {

        long start = System.nanoTime();
        int[][] dp = new int[N + 1][res + 1];
        for (int i = 0; i <= N; i++) {
            for (int j = 0; j <= res; j++) {
                dp[i][j] = -1;
            }
        }
        int result2 = process2(cur, aim, res, N, dp);
        long end = System.nanoTime();
        System.out.println(result2);
        System.out.println(end - start);


    }

    public static int process2(int cur, int aim, int res, int N, int[][] dp) {
        /*
        cur:当前位置
        aim:目标位置
        res:剩余步数
        N:共有哪些位置
         */

        if (dp[cur][res] != -1) {
            return dp[cur][res];
        }

        int ans = 0;
        if (res == 0) {
            ans = (cur == aim ? 1 : 0);
        } else if (cur == 1) {
            ans = process2(2, aim, res - 1, N, dp);
        } else if (cur == N) {
            ans = process2(N - 1, aim, res - 1, N, dp);
        } else {
            ans = process2(cur - 1, aim, res - 1, N, dp) + process2(cur + 1, aim, res - 1, N, dp);
        }
        dp[cur][res] = ans;
        return ans;

    }
	}

这种动态规划就是傻缓存,我并不关心你之前的状态,你只要给我当前的状态我就会给你个输出。就像是你只需要输入银行卡号和密码,银行就给你钱,他不关心你跟这个银行卡号和密码的故事。

这种方法的学名叫做:从顶向下的动态规划。 或者缓存法搜索。

4)代码优化2

在这里插入图片描述
上面这张图虽然画的很乱,但是很明了。
根据分析何以知道,如果

	1 2 3 4 5 
	N=5|cur=2|aim=4|res=6

在这种情况下,在res=0,cur=aim=4的时候方法才为1.

所以第4行第0列=1,即dp[4][0]=1;第0列的其余值均为0,即dp[i][0]=0,i≠4。

此处就相当于一个初始条件,也是我们之前学递归时候说的递归结束条件

那我们就不需要递归调用了,只需要根据表格的初始情况以及一些规律,把整张表格填写好了,就可以得出我们想要的结果了。

话不多说,上代码:

public class demo03 {

    static int res = 23;
    static int cur = 2;
    static int aim = 3;
    static int N = 7;

    public static void main(String[] args) {
        ways3(cur, aim, res, N);

    }

    public static void ways3(int cur, int aim, int res, int N) {

        System.out.println("第三种方式");
        long start = System.nanoTime();
        int[][] dp = new int[N + 1][res + 1];

        //第0列是初始条件
        dp[aim][0] = 1;//第0列其余的位置都是0

        for (int i = 1; i <= res; i++) {
            dp[1][i] = dp[2][i - 1];
            for (int j = 2; j < N; j++) {
                dp[j][i] = dp[j - 1][i - 1] + dp[j + 1][i - 1];
            }
            dp[N][i] = dp[N - 1][i - 1];
        }
        long end = System.nanoTime();
        System.out.println("共有方式:"+dp[cur][res]+"种");
        System.out.println("耗时为(ns):"+(end-start));
    }
}

初步入门动态规划就是这些了,我们接下来都要从初始的暴力动态规划,一步步推到这种状态转移过程,加油!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值