第9讲 深搜与简单的动态规划
- 格式:ppt
- 大小:371.50 KB
- 文档页数:48
动态规划算法的详细原理及使用案例一、引言动态规划是一种求解最优化问题的算法,它具有广泛的应用领域,如机器学习、图像处理、自然语言处理等。
本文将详细介绍动态规划算法的原理,并提供一些使用案例,以帮助读者理解和应用这一算法的具体过程。
二、动态规划的基本原理动态规划算法通过将问题分解为多个子问题,并利用已解决子问题的解来求解更大规模的问题。
其核心思想是利用存储技术来避免重复计算,从而大大提高计算效率。
具体来说,动态规划算法通常包含以下步骤:1. 定义子问题:将原问题分解为若干个子问题,这些子问题具有相同的结构,但规模更小。
这种分解可以通过递归的方式进行。
2. 定义状态:确定每个子问题的独立变量,即问题的状态。
状态具有明确的定义和可计算的表达式。
3. 确定状态转移方程:根据子问题之间的关系,建立状态之间的转移方程。
这个方程可以是简单的递推关系式、递归方程或其他形式的方程。
4. 解决问题:使用递推或其他方法,根据状态转移方程求解每个子问题,直到获得最终解。
三、动态规划的使用案例1. 背包问题背包问题是动态规划算法的经典案例之一。
假设有一个背包,它能容纳一定重量的物品,每个物品有对应的价值。
目的是在不超过背包总重量的前提下,选取最有价值的物品装入背包。
这个问题可以通过动态规划算法来求解。
具体步骤如下:(1)定义问题:在不超过背包容量的限制下,选取物品使得总价值最大化。
(2)定义状态:令dp[i][j]表示将前i个物品放入容量为j的背包中所能获得的最大价值。
(3)状态转移方程:dp[i][j] = max(dp[i-1][j-w[i]]+v[i], dp[i-1][j]),其中w[i]为第i个物品的重量,v[i]为第i个物品的价值。
(4)解决问题:根据状态转移方程依次计算每个子问题的解,并记录最优解,直到获得最终答案。
2. 最长公共子序列最长公共子序列(Longest Common Subsequence,简称LCS)是一种经典的动态规划问题,它用于确定两个字符串中最长的共同子序列。
动态规划的基本概念与方法动态规划(Dynamic Programming,简称DP)是解决一类最优化问题的一种方法,也是算法设计中的重要思想。
动态规划常用于具有重叠子问题和最优子结构性质的问题。
它将问题分解为子问题,并通过求解子问题的最优解来得到原问题的最优解。
动态规划的基本概念是“最优子结构”。
也就是说,一个问题的最优解可以由其子问题的最优解推导出来。
通过分解问题为若干个子问题,可以形成一个递归的求解过程。
为了避免重复计算,动态规划使用一个表格来保存已经计算过的子问题的解,以便后续直接利用。
这个表格也被称为“记忆化表”或“DP表”。
动态规划的基本方法是“状态转移”。
状态转移指的是,通过已求解的子问题的解推导出更大规模子问题的解。
常用的状态转移方程可以通过问题的递推关系定义。
通过定义好状态转移方程,可以通过迭代的方式一步步求解问题的最优解。
在动态规划中,通常需要三个步骤来解决问题。
第一步,定义子问题。
将原问题划分为若干个子问题。
这些子问题通常与原问题具有相同的结构,只是规模更小。
例如,对于计算斐波那契数列的问题,可以定义子问题为计算第n个斐波那契数。
第二步,确定状态。
状态是求解问题所需要的所有变量的集合。
子问题的解需要用到的变量就是状态。
也就是说,状态是问题(解决方案)所需要的信息。
第三步,确定状态转移方程。
状态转移方程通过已求解的子问题的解推导出更大规模子问题的解。
通常情况下,状态转移方程可以通过问题的递推关系确定。
在实际应用中,动态规划常用于求解最优化问题。
最优化问题可以归纳为两类:一类是最大化问题,另一类是最小化问题。
例如,最长递增子序列问题是一个典型的最大化问题,而背包问题是一个典型的最小化问题。
动态规划的优势在于可以解决许多复杂问题,并且具有可行的计算复杂度。
但是,动态规划也有一些限制。
首先,动态规划要求问题具有重叠子问题和最优子结构性质,不是所有问题都能够满足这两个条件。
其次,动态规划需要存储计算过的子问题的解,对于一些问题来说,存储空间可能会非常大。
背包问题的搜索解法《背包问题九讲》的本意是将背包问题作为动态规划问题中的一类进行讲解。
但鉴于的确有一些背包问题只能用搜索来解,所以这里也对用搜索解背包问题做简单介绍。
大部分以01背包为例,其它的应该可以触类旁通。
简单的深搜对于01背包问题,简单的深搜的复杂度是O(2^N)。
就是枚举出所有2^N种将物品放入背包的方案,然后找最优解。
基本框架如下:procedure SearchPack(i,cur_v,cur_w)if(i>N)if(cur_w>best)best=cur_wreturnif(cur_v+v[i]<=V)SearchPack(i+1,cur_v+v[i],cur_w+w[i])SearchPack(i+1,cur_v,cur_w)其中cur_v和cur_w表示当前解的费用和权值。
主程序中调用SearchPack(1,0,0)即可。
搜索的剪枝基本的剪枝方法不外乎可行性剪枝或最优性剪枝。
可行性剪枝即判断按照当前的搜索路径搜下去能否找到一个可行解,例如:若将剩下所有物品都放入背包仍然无法将背包充满(设题目要求必须将背包充满),则剪枝。
最优性剪枝即判断按照当前的搜索路径搜下去能否找到一个最优解,例如:若加上剩下所有物品的权值也无法得到比当前得到的最优解更优的解,则剪枝。
搜索的顺序在搜索中,可以认为顺序靠前的物品会被优先考虑。
所以利用贪心的思想,将更有可能出现在结果中的物品的顺序提前,可以较快地得出贪心地较优解,更有利于最优性剪枝。
所以,可以考虑将按照“性价比”(权值/费用)来排列搜索顺序。
另一方面,若将费用较大的物品排列在前面,可以较快地填满背包,有利于可行性剪枝。
最后一种可以考虑的方案是:在开始搜索前将输入文件中给定的物品的顺序随机打乱。
这样可以避免命题人故意设置的陷阱。
以上三种决定搜索顺序的方法很难说哪种更好,事实上每种方法都有适用的题目和数据,也有可能将它们在某种程度上混合使用。
NOIP从递归深搜到动态规划(C++)从递归深搜到动态规划提纲一、函数及其递归调用二、深度优先搜索三、动态规划一、函数及其递归调用一个C++程序无论大小都是由一个或多个“函数”组成。
其中必须有且只有一个main()函数,称之为“主函数”,由main()调用其它函数来完成程序的特定功能。
当然,其它函数之间也可以按照规则互相调用。
C++中的函数由一段相对独立的代码组成,这段代码能实现某一项具体、独立、完整的功能。
函数在程序设计中的作用主要有两个,一是“代码重用”,二是“问题分解”。
一、函数及其递归调用定义函数的格式如下:返回值类型函数名(参数列表){函数体}函数调用是通过“函数名”进行的,一般格式为:函数名(参数列表)一、函数及其递归调用函数调用方式有以下三种:(1)函数调用作为一条独立语句,完成一件事情,没有任何返回值。
例如:print(n);doit(dep,total);(2)函数调用的结果作为表达式的一部分。
例如:intt=compute(i,j)+ij;(3)以实参形式出现在其它函数调用中。
例如:number=min(sum(-5,100),n);num=max(max(a,b),c)。
一、函数及其递归调用例1、最大公约数【问题描述】输入两个正整数m和n,求它们的最大公约数。
【输入格式】一行两个正整数m和n,用一个空格隔开,2≤m,n≤10000。
【输出格式】一行一个整数,表示m和n的最大公约数。
【输入样例】2436【输出样例】12一、函数及其递归调用演示:欧几里德“辗转相除法”,体会其中的递归思想。
一、函数及其递归调用例2、分解质因子【问题描述】输入一个正整数n,用递归方法从小到大输出它的所有质因子(因子是质数)。
【输入格式】一行一个整数n,2≤n≤10000。
【输出格式】一行若干个整数,两数之间用一个空格隔开,从小到大输出。
【输入样例】18【输出样例】233一、函数及其递归调用如果n等于1,就没法再分解了。