蓝桥杯省赛必考题目之动态规划篇-1

1.动态规划入门篇

1.1 数字三角形初入版

请添加图片描述

如上图的一个数字三角形,题目要求是从顶部往底部走,寻找出到达底部的最短路径或最长路径,经典题目是寻找最长路径

有一个限制,这个限制是在上面往下面走的过程中,只能往左下走或者往右下走,直到底部。

题解思路,我们可以将题目进行翻转,题目要求从顶到底,那我们可以从底到顶,为什么这样设置,往下看就可以知道,设某一行坐标为(x,y)的点,从底部到(x,y)处的最长路径,要么是(x+1,y),就是(x+1,y+1) 加上本身的数值(x,y)那便有:

if(x == n){
	return dp[x][y] = triangle[x][y];
}else{
    return dp[x][y\] = Math.max(maxSum(x+1,y), maxSum(x+1,y+1)) + triangle[x\][y\];
}

dp[x][y] = Math.max(maxSum(x+1,y), maxSum(x+1,y+1)) + triangle[x][y];

dp数组是存储从底部到(x,y)的最长路径,maxSum是返回(x,y)最长路径的函数,tiangle就是那个数字三角形。

动态规划的基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。

动态规划具有无后效性,什么叫无后效性,简单理解就是现在产生的结果不由后面产生的结果有关,即产生了,就已经是最终结果了。

完整代码

import java.util.*;
class Soluton{
    static int[][] dp = new int[205][205];
    static int n = 0;
    static int[][] triangle = int[205][205];
    public static int maxSum(int x,int y){
        if(dp[x][y] != -1){
            return dp[x][y];
        }
        
        if(x == n){
            return dp[x][y] = triangle[x][y];
        }else{
            return dp[x][y] = Math.max(maxSum(i+1,y),maxSum(i+1,y+1)) + triangle[x][y];
        }
    }
}

同理如果要求最小的路径也是一样的。

题目连接:https://leetcode-cn.com/problems/triangle/

1.2 数字三角形的栈优化

我们知道我们有时候在写题的时候,都会出现段错误等错误,那当我们如果递归的层次过多的时候,我们是否同样会产生栈溢出,那我们能不能不用递归,而用递推,这下总不会产生栈溢出错误了吧。那我们来进行优化一下吧

如果我们从底部开始往上建房子,最底层的地基,已经是固定的了,不能动,往上建房子的时候,每次只能选择左下的材料,或右下的材料,我们要选择最好的。第二层便也随着建立起来了,那每层代表的是什么呢?即dp[x][y]中的xy代表的什么呢?代表的是从地基开始到xy这个点使用的最好材料的总值。那我们一直求到最顶层,不就是建这个房子用材料最大(最好)值了吗?下面我们来coding!

import java.util.*;
public class Main{
    static Scanner sc = new Scanner(System.in);
    public static void main(String[] args){
        int n = sc.nextInt();
        int[][] dp = new int[n+5][n+5];
        int[][] triangle = new int[n+5][n+5];
        for(int i = 0; i < n; ++i){
            for(int j = 0; j <= i; ++j){
                triangle[i][j] = sc.nextInt();
            }
        }
        for(int i = 0; i < n; ++i){
            dp[0][i] = triangle[n-1][i];
        }
        for(int i = 1; i < n; ++i){
            for(int j = 0; j < n - i; ++j){
                dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j+1]) + triangle[n - i - 1][j];
            }
        }
        System.out.println(dp[n-1][0]);
    }
}

1.3 数字三角形的内存优化

处理完段错误,即栈溢出错误后,我们应该思考,是否存在更好的算法,时间上的缩减,亦或是空间上的缩减呢?我们会发现我们从下往上建房子,在建完下面一层后,下面一层是不是没有用处了,那我们能不能以上面一层覆盖下面一层呢?即将空间复杂度由O(n2)缩减为O(n)

7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

如这个数字三角形,在倒数第二行的2的时候,比较4和5,使用5+2=7后,倒数第一行4这个值是不是无法再次使用到了,在计算7(倒数第二行的7)这个点和谁相加的时候,只会使用到正下方和右下方的数字。那我们就可以大胆进行覆盖了。

Coding

import java.util.*;
public class Main{
    static Scanner sc = new Scanner(System.in);
    public static void main(String[] args){
        int n = sc.nextInt();
        int[] dp = new int[n+5];
        int[][] triangle = new int[n+5][n+5];
        for(int i = 0; i < n; ++i){
            for(int j = 0; j <= i; ++j){
                triangle[i][j] = sc.nextInt();
            }
        }
        for(int i = 0; i < n; ++i){
            dp[i] = triangle[n-1][i];
        }
        for(int i = 1; i < n; ++i){
            for(int j = 0; j < n - i; ++j){
                dp[j] = Math.max(dp[j],dp[j+1]) + triangle[n - i - 1][j];
            }
        }
        System.out.println(dp[0]);
    }
}

后面总结引用其他博主的总结方法:

递归到动规的一般转化方法

递归函数有n个参数,就定义一个n维的数组,数组的下标是递归函数参数的取值范围,数组元素的值是递归函数的返回值,这样就可以从边界值开始, 逐步填充数组,相当于计算递归函数值的逆过程。

动规解题的一般思路

1. 将原问题分解为子问题

把原问题分解为若干个子问题,子问题和原问题形式相同或类似,只不过规模变小了。子问题都解决,原问题即解决(数字三角形例)。
子问题的解一旦求出就会被保存,所以每个子问题只需求 解一次。
2.确定状态

在用动态规划解题时,我们往往将和子问题相关的各个变量的一组取值,称之为一个“状 态”。一个“状态”对应于一个或多个子问题, 所谓某个“状态”下的“值”,就是这个“状 态”所对应的子问题的解。
所有“状态”的集合,构成问题的“状态空间”。“状态空间”的大小,与用动态规划解决问题的时间复杂度直接相关。 在数字三角形的例子里,一共有N×(N+1)/2个数字,所以这个问题的状态空间里一共就有N×(N+1)/2个状态。
整个问题的时间复杂度是状态数目乘以计算每个状态所需时间。在数字三角形里每个“状态”只需要经过一次,且在每个状态上作计算所花的时间都是和N无关的常数。

3.确定一些初始状态(边界状态)的值

以“数字三角形”为例,初始状态就是底边数字,值就是底边数字值。

4. 确定状态转移方程

 定义出什么是“状态”,以及在该“状态”下的“值”后,就要找出不同的状态之间如何迁移――即如何从一个或多个“值”已知的 “状态”,求出另一个“状态”的“值”(递推型)。状态的迁移可以用递推公式表示,此递推公式也可被称作“状态转移方程”。

数字三角形的状态转移方程:

能用动规解决的问题的特点

1) 问题具有最优子结构性质。如果问题的最优解所包含的 子问题的解也是最优的,我们就称该问题具有最优子结 构性质。

2) 无后效性。当前的若干个状态值一旦确定,则此后过程的演变就只和这若干个状态的值有关,和之前是采取哪种手段或经过哪条路径演变到当前的这若干个状态,没有关系。

————————————————
版权声明:本文为CSDN博主「ChrisYoung1314」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/baidu_28312631/article/details/47418773

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蜡笔里没小新诶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值