CSDN官方创作助手InsCode AI 教你分分钟搞定一篇好文章

news2024/12/25 9:19:59

CSDN官方推出创作助手InsCode AI很多天了,有心人都能发现,在写作界面的右上角多了一个创作助手的浮动按钮,点击后出现如下界面:

现阶段是“限免”状态,不好好利用它来创作,就有点辜负CSDN官方大佬们的良苦用心了。

以主题“动态规划法”写一篇博客,以下是我用创作助手InsCode AI辅助写作的全过程,教你分分钟搞定一篇“好”文章!

用法简介

1. 以主题线索列举几个提问,比如“何为动态规划法”、“详细介绍一下动态规划算法”、“写一个动态规划算法并做大量注解”。

2. 把InsCode AI的答案插入写作文本框,整理之后自拟各段文字的多级标题。

3. 最后写段文章小结,插入目录,添加插图美化一下,保存草稿。

快速搞定一篇!正文如下:


目录

动态规划法

定义

条件

步骤

简单实例

题目描述

算法实现

代码实现 

进阶实例

题目描述

1. 基于递推的自底向上方法

2. 基于记忆化搜索的自顶向下方法

本文小结


动态规划法

定义

全称为 Dynamic Programming,缩写为DP。是一种常用的解决多阶段决策问题的数学算法。它通常用于求解具有某种最优性质的问题,比如最大值、最小值等等。

动态规划算法通常基于一个递推公式以及一个或多个初始状态。当前子问题的解将由上一次子问题的解推出来。动态规划算法在处理问题时,会把子问题的解缓存起来,这样以后再遇到同样的子问题时可以直接查表而不必重新计算。这种方式可以避免重复计算,减少计算量,提高算法效率。

动态规划算法在求解各种经济、管理、信息、技术等方面的优化问题中有广泛应用。

条件

一般来说,动态规划问题会满足以下几个条件:

  1. 有重叠子问题:即一个问题的子问题是重复的,需要不断进行重复计算。

  2. 子问题具有最优子结构:即子问题之间相互独立,且子问题的最优解能够推导出原问题的最优解。

  3. 无后效性:即某个状态一旦确定,就不受之后决策的影响。

步骤

动态规划(Dynamic Programming)是一种解决多阶段决策过程最优化问题的数学方法,它将原问题分解为相对简单的子问题,并采用递推的方式求解子问题,最终得到原问题的最优解。动态规划算法分为两种类型:一种是基于记忆化搜索的自顶向下的方法,另一种是基于递推的自底向上的方法。在这两种方法中,都需要定义状态和状态转移方程,以确定子问题间的递推关系。具体来说,我们可以通过以下步骤来设计动态规划算法:

  1. 确定状态:找到问题中的最简单的子问题,列出状态表示,比如最大子序列问题中,状态可能表示为以第i个数为结尾的最大子序列和。

  2. 确定状态转移方程:列出状态转移方程,即当前子问题的最优解如何由前一个子问题的最优解得到。

  3. 确定初始状态:确定所有子问题中最简单的状态的解,通常是边界状态。

  4. 确定计算顺序:根据状态转移方程,确定计算的顺序,通常是按照状态的维度进行计算。

  5. 优化内存空间:如果状态转移只与前一个状态有关,则可以不必缓存所有状态,只需要缓存前一个状态即可。

简单实例

下面以 LeetCode 509题 "斐波那契数" 为例,给出一个动态规划算法并加上详细的注解。

题目描述

斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。即:

F(0) = 0, F(1) = 1 F(n) = F(n - 1) + F(n - 2), 其中 n > 1.

示例 1:

输入: 2 输出: 1 解释: F(2) = F(1) + F(0) = 1 + 0 = 1.

示例 2:

输入: 3 输出: 2 解释: F(3) = F(2) + F(1) = 1 + 1 = 2.

示例 3:

输入: 4 输出: 3 解释: F(4) = F(3) + F(2) = 2 + 1 = 3.

算法实现

  1. 确定状态:根据题目描述,可以定义一个数组 dp[] 来表示斐波那契数列中前n个数字的值,dp[i]表示斐波那契数列中第i个数字的值。

  2. 确定状态转移方程:根据斐波那契数列的定义,dp[i] = dp[i-1] + dp[i-2],其中i > 1。

  3. 确定初始状态:根据斐波那契数列的定义,dp[0] = 0,dp[1] = 1。

  4. 确定计算顺序:从左到右依次计算dp[2]、dp[3]、……、dp[n]。

  5. 优化内存空间:由于状态转移只与前两个状态有关,因此可以只用两个变量来记录前两个状态的值,不必缓存所有状态。

代码实现 

下面是完整的算法实现,每一行都有注释说明。

package main

import "fmt"

func fib(n int) int {
	if n < 2 {   // 如果n为0或1,直接返回n
		return n
	}
	dp := [2]int{0, 1}   // 定义初始状态,dp[0]表示F(0),dp[1]表示F(1)
	for i := 2; i <= n; i++ {   // 从2到n按照状态转移方程求解
		dp_i := dp[0] + dp[1]   // 计算dp[i],即F(i)
		dp[0] = dp[1]   // 更新前两个状态
		dp[1] = dp_i
	}
	return dp[1]   // 返回最终结果
}

func main() {
	fmt.Println(fib(2)) // 1
	fmt.Println(fib(3)) // 2
	fmt.Println(fib(4)) // 3
}

该算法的时间复杂度为 O(n),空间复杂度为 O(1)。时间复杂度线性,空间复杂度常数级别,因此在实际应用中较为实用。

进阶实例

下面以 LeetCode 1143题 "最长公共子序列" (LCS)为例,运用动态规划两种类型分别实现:

题目描述

给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

  • 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

示例 1:

输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace" ,它的长度为 3 。

示例 2:

输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3 。

示例 3:

输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0 。

提示:

  • 1 <= text1.length, text2.length <= 1000
  • text1 和 text2 仅由小写英文字符组成。

1. 基于递推的自底向上方法

在该方法中,我们从子问题开始,依次计算出所有的子问题,最终得到原问题的答案。具体地,我们使用一个数组来记录子问题的答案,然后根据子问题的结果计算更大的问题的答案,直到求解出原问题的答案。

例如,在求解两个字符串的最长公共子序列问题时,我们可以定义一个二维的数组dp[i][j],表示计算字符串1的前i个字符和字符串2的前j个字符的最长公共子序列。我们首先将数组中所有的元素初始化为0,然后依次遍历字符串1和字符串2的所有字符,根据当前字符是否相等来更新数组中的元素:

  • 如果第i个字符和第j个字符相同,则最长公共子序列长度加1,即dp[i][j] = dp[i-1][j-1] + 1
  • 如果第i个字符和第j个字符不同,则最长公共子序列长度等于前一个状态中两个字符串中较长的那个字符串的最长公共子序列长度,即dp[i][j] = max(dp[i-1][j], dp[i][j-1])

最终,dp[m][n]就是字符串1和字符串2的最长公共子序列的长度,其中m和n分别为两个字符串的长度。代码如下:

package main

import "fmt"

func MaxLCS(s1, s2 string) int {
	m, n := len(s1), len(s2)
	dp := make([][]int, m+1)
	for i := range dp {
		dp[i] = make([]int, n+1)
	}
	for i := 1; i <= m; i++ {
		for j := 1; j <= n; j++ {
			if s1[i-1] == s2[j-1] {
				dp[i][j] = dp[i-1][j-1] + 1
			} else {
				dp[i][j] = max(dp[i-1][j], dp[i][j-1])
			}
		}
	}
	return dp[m][n]
}

func max(x, y int) int {
	if x > y {
		return x
	}
	return y
}

func main() {
	text1 := "abcde"
	text2 := "ace"
	fmt.Println(MaxLCS(text1, text2))
	text1 = "abc"
	text2 = "abc"
	fmt.Println(MaxLCS(text1, text2))
	text1 = "abc"
	text2 = "def"
	fmt.Println(MaxLCS(text1, text2))
}

2. 基于记忆化搜索的自顶向下方法

在该方法中,我们使用递归函数和一个备忘录来实现动态规划。递归函数的基本思想是把原问题划分成若干个子问题,每个子问题都解决一次,然后将其结果缓存,避免重复计算。备忘录记录了已经计算过的子问题的答案,如果当前问题之前已经被解决过,则直接返回备忘录中的结果。

首先定义了一个 lcs 函数,用于递归寻找最长公共子序列。在该函数中,我们需要传入两个字符串 s1 和 s2、以及当前的遍历下标(i 和 j)和记忆化数组 memo。memo 用于存储已经遍历过的字符串长度信息,避免重复计算公共子序列,因为计算公共子序列的递归过程中存在大量的重复计算,使用 memo 记录已有的计算结果可以大大提高计算效率。在进行计算前,我们首先判断当前查询的两个字符串是否为空,如果是,则直接返回 0。如果当前条件已经对应 memo 数组中的值,则直接返回对应结果。

之后,我们通过比较当前遍历的 s1 和 s2 字符串的最后一个字符(即 i-1 和 j-1)是否相等,分别进行判断。如果相等,则将最后一个字符放入公共子序列中,长度加 1,递归向前寻找下一个字符;如果不相等,则通过在 s1 中移去最后一个字符或在 s2 中移去最后一个字符,分别计算得到两种情况下的公共子序列,并将它们的长度比较大小,返回最大的那个长度。

在 MaxLCS 函数中,我们定义了一个 memo 数组,用于存储已经遍历过的字符串长度信息。在循环初始化 memo 数组时,我们将各个位置的值设为 -1,表示尚未进行过计算。最后,我们将 s1 和 s2 的两个尾部下标(即 len(s1) 和 len(s2))以及 memo 数组传入 lcs 函数中,得到最终的最长公共子序列长度。

代码如下:

package main

import "fmt"

func lcs(s1, s2 string, i, j int, memo [][]int) int {
	if i == 0 || j == 0 {
		return 0
	}
	if memo[i][j] != -1 {
		return memo[i][j]
	}
	if s1[i-1] == s2[j-1] {
		memo[i][j] = lcs(s1, s2, i-1, j-1, memo) + 1
	} else {
		memo[i][j] = max(lcs(s1, s2, i-1, j, memo), lcs(s1, s2, i, j-1, memo))
	}
	return memo[i][j]
}

func MaxLCS(s1, s2 string) int {
	memo := make([][]int, len(s1)+1)
	for i := range memo {
		memo[i] = make([]int, len(s2)+1)
		for j := range memo[i] {
			memo[i][j] = -1
		}
	}
	return lcs(s1, s2, len(s1), len(s2), memo)
}

func max(x, y int) int {
	if x > y {
		return x
	}
	return y
}

func main() {
	text1 := "abcde"
	text2 := "ace"
	fmt.Println(MaxLCS(text1, text2))
	text1 = "abc"
	text2 = "abc"
	fmt.Println(MaxLCS(text1, text2))
	text1 = "abc"
	text2 = "def"
	fmt.Println(MaxLCS(text1, text2))
}

本文小结

动态规划是一种通过将原问题拆分成子问题来解决复杂问题的算法。在动态规划中,通过记录之前计算的结果,可以避免重复的计算,从而提高算法效率。

在动态规划问题中,通常需要定义状态,确定状态转移方程和边界条件。通过状态转移方程可以将问题分解为规模更小的子问题,并将子问题的解决结果存储在一个表格中,以方便后续的计算。

动态规划常用于解决最长公共子序列、最长递增子序列、背包问题、字符串编辑距离等问题。在解决这些问题时,我们需要通过分析问题的特殊性质,设计出符合实际情况的状态和状态转移方程。

需要注意的是,在设计状态转移方程时,需要注意计算顺序,尤其是当某个状态的计算依赖于前面的多个状态时,需要仔细排列计算顺序,避免出现错误的结果。

总之,动态规划是一种有效的算法思想,可以解决很多实际问题。虽然需要一定的思维难度和技巧,但是只要掌握了基本原理和方法,就可以灵活地应用到各种场景中,解决各种问题。


整理完成后,先别急着发表,鼠标移动到最下方按钮“保存草稿”,出现“保存并预览”后点击它,得到文章链接,复制粘贴到CSDN质量分查询页面里查分:

https://www.csdn.net/qc

不错,果然是"好"文章!可以发表了,如果感觉本文有点帮助,请收藏点赞,写个评论。谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/546902.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【王道·计算机网络】第五章 传输层【未完】

一、传输层概述 传输层为应用层提供通信服务&#xff0c;使用网络层服务传输层的功能&#xff1a; 提供进程和进程之间的逻辑通信&#xff08;网络层提供主机之间的逻辑通信&#xff09;复用&#xff08;发送发不同的应用进程&#xff09;和分用&#xff08;接收方正确的数据传…

Primer C++(第三章)

补码、原码和反码 正数的原码、反码、补码都相同 负数的补码&#xff1a;1、正数的原码符号位由0变1 &#xff08;负数的原码&#xff09; 2、对负数的原码除符号位外每位取反 &#xff08;负数的反码&#xff09; 3、对负数的反码末尾1 &#xff08;负数的补码&#xff09; …

众位力量汇集《永恒之塔私服》新版龙战前传

盛大游戏《永恒之塔》从万众翘首企盼中登陆国服到现在&#xff0c;已经过去了一年有余。在前不久前更新的周年庆版本“云上的召唤”中&#xff0c;精灵星的宝宝终于可以和精灵星一起翱翔在天际了…… “云上的召唤”我们还没有体验够&#xff0c;全新版本“龙战前传”已然于7月…

【观察】从业界首款“空间穿越屏”,看华为全屋智能的进化与重构

这个时代&#xff0c;“家”的构成不再是简单的一家三口&#xff0c;客厅、厨房、卧室也不再只是承担某个单一功能或场景的空间。 无数身在异乡打拼的青年&#xff0c;开始向往一个专属的独立空间&#xff1b;那些奔波劳碌的中年夫妻&#xff0c;在为家人创造更好生活环境的同时…

RabbitMQ_面试题01

文章目录 1.RabbitMQ如何防止消息堆积2.RabbitMQ如何保证消息顺序消费3.RabbitMQ如何防止消息重复消费4.RabbitMQ如何保证消息可靠性4.1 消息持久化4.2 生产者确认2.2.1 application.yml2.2.2 Config2.2.3 Test 4.3 消费者确认4.3.1 application.yml4.3.2 Test 1.RabbitMQ如何防…

OPT (奥普特)锂电池视觉检测技术精彩亮相CIBF

5月16&#xff5e;18日&#xff0c;第十五届中国国际电池技术展览会在深圳举办&#xff0c;全球2500多家优秀电池企业参展。 OPT&#xff08;奥普特&#xff09;作为锂电行业机器视觉核心供应商&#xff0c;携3D、深度学习、分频技术等视觉检测技术亮相&#xff0c;并展示了上…

chatgpt赋能Python-python3免费吗

Python3免费吗&#xff1f; Python3到底免费还是收费呢&#xff1f;这是一个被许多人关注和疑惑的问题。本文将从不同方面解答这个问题&#xff0c;希望能给你提供一个清晰的认识。 什么是Python3&#xff1f; Python3是一种通用、高级、解释型的编程语言。它是由Guido van …

【Linux初阶】fork进程创建 进程终止 进程等待

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【Linux初阶】 ✒️✒️本篇内容&#xff1a;fork进程创建&#xff0c;理解fork返回值和常规用法&#xff0c;进程终止&#xff08;退出码、退出场景、退出…

第08章_聚合函数

第08章_聚合函数 我们上一章讲到了 SQL 单行函数。实际上 SQL 函数还有一类&#xff0c;叫做聚合&#xff08;或聚集、分组&#xff09;函数&#xff0c;它是对一组数据进行汇总的函数&#xff0c;输入的是一组数据的集合&#xff0c;输出的是单个值。 1. 聚合函数介绍 什么是…

【sentinel】Sentinel工作主流程以流控规则源码分析

Sentinel工作主流程 在Sentinel里面&#xff0c;所有的资源都对应一个资源名称&#xff08;resourceName&#xff09;&#xff0c;每次资源调用都会创建一个Entry对象。Entry可以通过对主流框架的适配自动创建&#xff0c;也可以通过注解的方式或调用SphU API显式创建。Entry创…

跨境seo引流的13种方法

跨境SEO引流是一种通过搜索引擎优化来吸引国际目标受众并增加网站流量的策略。以下是一些跨境SEO引流的关键步骤和技巧&#xff1a; 目标受众研究&#xff1a;了解你的目标市场和受众群体。了解他们的需求、喜好、购买习惯以及使用的搜索引擎和关键词。这将帮助你确定你的跨境S…

chatgpt赋能Python-python3怎么合并列表

Python3&#xff1a;合并列表的不同方法 如果你正在使用Python 3&#xff0c;那么你很可能会面对合并列表的问题。合并列表&#xff08;也称为连接列表或串联列表&#xff09;是将两个或多个列表组合成一个列表的过程&#xff0c;这是在编程中很常见的任务。在这篇文章里&…

Python并发编程:异步编程和多线程技术的应用和效率优化

第一章&#xff1a;介绍 在当今的软件开发领域&#xff0c;高效的并发编程对于处理大规模数据和提升系统性能至关重要。Python作为一种简洁、易读且功能强大的编程语言&#xff0c;提供了多种并发编程的技术和工具。本文将深入探讨Python中的异步编程和多线程技术&#xff0c;…

chatgpt赋能Python-python3如何画图

Python3如何画图&#xff1f; Python是一种高级编程语言&#xff0c;它有着多种用途&#xff0c;包括数据分析和可视化。Python3是Python的最新版本&#xff0c;它具有更好的性能和易用性。在这篇文章中&#xff0c;我们将介绍如何使用Python3来画图&#xff0c;并探讨其优势和…

KingbaseES 逻辑读与物理读

oracle数据库中逻辑读&#xff0c;物理读 数据访问方式&#xff1a;数据库少不了和操作系统进行数据交互&#xff0c;表数据最好的方式是从数据库共享池中访问到&#xff0c;避免发生磁盘IO&#xff0c;当然如果共享池中没有访问到数据就难免发生磁盘IO。 物理读&#xff1a;从…

第三篇、Arduino uno、nano、2560用oled0.96寸屏幕显示dht11温湿度传感器的温度和湿度信息——结果导向

0、结果 说明&#xff1a;先来看看拍摄的显示结果&#xff0c;如果是你想要的&#xff0c;可以接着往下看。 1、外观 说明&#xff1a;本次使用的oled是0.96寸的&#xff0c;别的规格的屏幕不一定适用本教程&#xff0c;一般而言有显示白色、蓝色和蓝黄一起显示的&#xff0…

【小沐学Web】Node实现Web图表功能(ECharts.js,React)

&#x1f388;&#x1f388;&#x1f388;Python实现Web图表功能系列&#xff1a;&#x1f388;&#x1f388;&#x1f388;1&#x1f388;【Web开发】Python实现Web图表功能&#xff08;D-Tale入门&#xff09;&#x1f388;2&#x1f388;【Web开发】Python实现Web图表功能&a…

Fragment 要你何用?2.0版本

前言 在之前的文章里有分析过Activity、Fragment、View之间的关联&#xff0c;也简单分析了Fragment的原理。 本篇将对Fragment被高频使用的场景以及一些坑点作分析&#xff0c;通过本篇文章&#xff0c;你将了解到&#xff1a; 老生常谈&#xff1a;为什么需要Fragment?Frag…

Java 创建一个大文件

有时候&#xff0c;我们在对文件进行测试的时候&#xff0c;可能需要创建一个临时的大文件。 那么问题来了&#xff0c;在 Java 中如何创建大文件呢&#xff1f; 问题和解决 有些人想到的办法就是定义一个随机的字符串&#xff0c;然后重复很多次&#xff0c;然后将这个字符…

第一篇:强化学习基本原理通俗介绍

你好&#xff0c;我是zhenguo&#xff08;郭震&#xff09; 今天强化学习第一篇&#xff1a;白话介绍强化学习的基本原理 强化学习是一种机器学习方法&#xff0c;旨在让智能体&#xff08;agent&#xff09;通过与环境的交互学习如何做出最优的行动选择以获得最大的累积奖励。…