当前位置: 首页 > news >正文

数据结构与算法 -- 动态规划常见问题

一、简单的路径规划

1、问题描述

问题:一个机器人位于一个 m * n 网格的左上角 (起始点在下图中标记为“开始” ),机器人每次只能向下或者向右移动一步,现在机器人试图达到网格的右下角(在下图中标记为“结束”)。问总共有多少条不同的路径?

                               


示例:

输入:m = 3, n = 2
输出: 3 
解释: 从左上角开始,总共有 3 条路径可以到达右下角:
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右

2、算法分析  

1)备忘录

        dp[i][j]表示到i行j列的格子的路径数; 

2)初始化状态

        因为每次只能向下或者向右进行移动,所以第一行第一列的所有路径数都是1;其他格子路劲数初始化为0; 

3)状态参数

        状态参数就是i行j列; 

4)决策

        因为只能向下或者向右移动,因此d[i][j] = d[i-1][j] + d[i][j-1] 

3、状态转移方程

                            

4、算法实现

package main

import "fmt"


func DP(m int, n int) int {
	// 创建备忘录
	dp := make([][]int, m)
	for i := range dp{
		dp[i] = make([]int, n)
	}

	// 初始化状态
	for i := 0; i < m; i++ {
		dp[i][0] = 1
	}
	for i := 0; i < n; i++ {
		dp[0][i] = 1
	}

	// 转移方程转换实现
	for i := 0; i< m; i++ {
		for j := 0; j < n; j++ {
			if i == 0 || j == 0 {
				dp[i][j] = 1
			} else {
				dp[i][j] = dp[i-1][j] + dp[i][j-1]
			}
		}
	}
	return dp[m-1][n-1] // 输出答案
}

func main() {
	m := 3
	n := 7
	fmt.Println(DP(m, n))  // 输出答案
}

二、带路障路径规划

1、问题描述

问题:一个机器人位于一个 m * n 网格的左上角 (起始点在下图中标记为“开始” )。机器人每次只能向下或者向右移动一步,现在机器人试图达到网格的右下角(在下图中标记为“结束”)。考虑网格中有障碍物,网格中的障碍物和空位置分别用 1 和 0 来表示,那么从左上角到右下角将会有多少条不同的路径?

 


示例:

输入:
[ 
  [0, 0, 0], 
  [0, 1, 0], 
  [0, 0, 0] 
]
输出: 2
解释:3 * 3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

 2、算法分析  

1)备忘录

         dp[i][j]表示到i行j列的格子的路径数; 

2)初始化状态

        依然先考虑网格的第一行和第一列,第一行永远只能从左侧的格子往前走;第一列永远只能从上方的格子往下走。由于我们只能向右或向下走,所以第一行和第一列的格子永远只能存在 1 条路径。但是,我们还需要再考虑那些有障碍的格子,对这些格子来说,它们的路径总数应该是 0 而不是 1。

3)状态参数

        状态参数就是i行j列; 状态参数依然是格子的行数和列数;

4)决策

          因为只能向下或者向右移动,因此d[i][j] = d[i-1][j] + d[i][j-1] ;有障碍物的格子的路径数为0;

3、状态转移方程

 

4、算法实现

package main

import "fmt"


func DP(m int, n int, array [][]int) int {
	// 创建备忘录
	dp := make([][]int, m)
	for i := range dp{
		dp[i] = make([]int, n)
	}

	// 初始化状态
	for i := 0; i < m; i++ {
		if array[i][0] == 1 {
			dp[i][0] = 0
		} else {
			dp[i][0] = 1
		}
	}
	for i := 0; i < n; i++ {
		if array[0][i] == 1 {
			dp[0][i] = 0
		} else {
			dp[0][i] = 1
		}
	}

	// 转移方程转换实现
	for i := 0; i< m; i++ {
		for j := 0; j < n; j++ {
			if (i == 0 || j == 0) && array[i][j] == 0 {
				dp[i][j] = 1
			} else if array[i][j] == 1 {
				dp[i][j] = 0
			} else {
				dp[i][j] = dp[i-1][j] + dp[i][j-1]
			}
		}
	}
	return dp[m-1][n-1] // 输出答案
}

func main() {
	m := 3
	n := 3
	array := [][]int{{0, 0, 0},
		             {0, 1, 0},
		             {0, 0, 0}}
	fmt.Println(DP(m, n, array))  // 输出答案
}

三、跳跃游戏

1、问题描述

题目:给出一个非负整数数组 A,你最初定位在数组的第一个位置。数组中的每个元素代表你在那个位置可以跳跃的最大长度。判断你是否能到达数组的最后一个位置。 


示例1:

输入:A = [2, 3, 1, 1, 6]
输出: True
解释: 我们可以先跳 1 步,从位置 0 到达位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。

2、算法分析  

1)备忘录

         DP[i] 来表示能否从出发点到达位置 i。 

2)初始化状态

        初始化状态就是 0 这个位置。因为这个位置是出发点,因此肯定可以到达,所以我们可以将其初始化成 True。 

3)状态参数

        状态参数也比较容易看出,只有数组的位置是变化的,因此状态参数就是当前位置 i 

4)决策

        要知道能否到达位置 i,就需要逐个看前面的位置,判定能否从位置 i−1、i−2、i−3 … 跳到位置 i 上。 

3、状态转移方程

 

4、算法实现

package main

import "fmt"


func DP(array []int) bool {
	length := len(array)
	// 创建备忘录
	dp := make([]bool, length)

	// 初始化状态
	for i := 0; i < length; i++ {
		dp[i] = false
	}
	dp[0] = true

	// 转移方程转换实现
	for i := 1; i< length; i++ {
		for j := 0; j < i; j++ {
			if dp[j] && j + array[j] >= i {
				dp[i] = true
				break
			}
		}
	}
	return dp[length-1] // 输出答案
}

func main() {
	array := []int{2, 3, 1, 1, 6}
	fmt.Println(DP(array))  // 输出答案
}

 

声明:本文参考极客时间《动态规划面试宝典》

相关文章:

  • 3.ICMP
  • 尚好房 09_权限管理
  • java面向对象(一)
  • Allegro Design Entry HDL(OrCAD Capture HDL)工具菜单详细介绍
  • Java 线程及线程池的创建方式
  • 分布式ID生成服务
  • Vue中的条件渲染v-if、v-show
  • 【Spring Boot】响应JSON实现原理
  • 基于51单片机交通信号灯仿真_东西管制+南北管制
  • 2022“杭电杯”中国大学生算法设计超级联赛(4)
  • AngelScript -- C++程序最好的脚本语言
  • 如何编写整洁的代码
  • leetcode: 122. 买卖股票的最佳时机II
  • 字符串习题总结3
  • Java 操作RestHighLevelClient查询详解
  • JS中 map, filter, some, every, forEach, for in, for of 用法总结
  • 【面试系列】之二:关于js原型
  • cookie和session
  • js算法-归并排序(merge_sort)
  • laravel 用artisan创建自己的模板
  • Spring Boot MyBatis配置多种数据库
  • 创建一种深思熟虑的文化
  • 给第三方使用接口的 URL 签名实现
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 爬虫模拟登陆 SegmentFault
  • 容器服务kubernetes弹性伸缩高级用法
  • 如何优雅地使用 Sublime Text
  • 数据结构java版之冒泡排序及优化
  • ​什么是bug?bug的源头在哪里?
  • (12)Linux 常见的三种进程状态
  • (23)Linux的软硬连接
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (八)Docker网络跨主机通讯vxlan和vlan
  • (二)构建dubbo分布式平台-平台功能导图
  • (附源码)spring boot基于Java的电影院售票与管理系统毕业设计 011449
  • (附源码)springboot建达集团公司平台 毕业设计 141538
  • (附源码)ssm基于jsp的在线点餐系统 毕业设计 111016
  • (五)MySQL的备份及恢复
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • .bat批处理(七):PC端从手机内复制文件到本地
  • .Net Core与存储过程(一)
  • .NET 应用启用与禁用自动生成绑定重定向 (bindingRedirect),解决不同版本 dll 的依赖问题
  • .NET(C#、VB)APP开发——Smobiler平台控件介绍:Bluetooth组件
  • .NET/C# 使用 ConditionalWeakTable 附加字段(CLR 版本的附加属性,也可用用来当作弱引用字典 WeakDictionary)
  • .Net各种迷惑命名解释
  • .net开发时的诡异问题,button的onclick事件无效
  • .NET应用架构设计:原则、模式与实践 目录预览
  • .NET中的Event与Delegates,从Publisher到Subscriber的衔接!
  • @angular/cli项目构建--http(2)
  • @基于大模型的旅游路线推荐方案
  • []常用AT命令解释()
  • [AHOI2009]中国象棋 DP,递推,组合数
  • [ArcPy百科]第三节: Geometry信息中的空间参考解析
  • [bzoj4010][HNOI2015]菜肴制作_贪心_拓扑排序
  • [BZOJ4554][TJOI2016HEOI2016]游戏(匈牙利)