《算法分析与设计技巧》课件第三章.pptx

上传人(卖家):momomo 文档编号:7676870 上传时间:2024-07-06 格式:PPTX 页数:83 大小:2.21MB
下载 相关 举报
《算法分析与设计技巧》课件第三章.pptx_第1页
第1页 / 共83页
《算法分析与设计技巧》课件第三章.pptx_第2页
第2页 / 共83页
《算法分析与设计技巧》课件第三章.pptx_第3页
第3页 / 共83页
《算法分析与设计技巧》课件第三章.pptx_第4页
第4页 / 共83页
《算法分析与设计技巧》课件第三章.pptx_第5页
第5页 / 共83页
点击查看更多>>
资源描述

1、第三章动态规划3.13.1动态规划的基本思想与概念动态规划的基本思想与概念3.23.2动态规划的简单应用动态规划的简单应用3.33.3动态规划的深入研究动态规划的深入研究3.43.4动态规划的优化方法动态规划的优化方法【3.1.13.1.1动态规划的基本思想动态规划的基本思想】1951年美国数学家R.Bellman等人,根据一类多阶段问题的特点,把多阶段决策问题变换为一系列互相联系的单阶段问题,然后逐个加以解决。一些静态模型,只要人为地引进“时间”因素,分成时段,就可以转化成多阶段的动态模型,用动态规划方法去处理。与此同时,他提出了解决这类问题的“最优化原理”(Principleofoptim

2、ality):“个过程的最优决策具有这样的性质,即无论其初始状态和初始决策如何,其今后诸策略对以第一个决策所形成的状态作为初始状态的过程而言,必须构成最优策略。”简言之,一个最优策略的子策略,对于它的初态和终态而言也必是最优的。这个“最优化原理”如果用数学化一点的语言来描述的话,就是假设为了解决某一优化问题,需要依次作出n个决策D1、D2、Dn,若这个决策序列是最优的,对于任何一个整数k,1kn,不论前面k个决策是怎样的,以后的最优决策只取决于由前面决策所确定的当前状态,即以后的决策Dk+1、Dk+2、Dn也是最优的。最优化原理是动态规划的基础。任何一个问题,如果失去了这个最优化原理的支持,就

3、不可能用动态规划方法计算。所以说,它只适于解决一定条件的最优策略问题。或许,大家听到这个结论会很失望。其实,这个结论并没有削减动态规划的光辉,因为属于上面范围内的问题极多,还有许多看似不是这个范围中的问题都可以转化成这类问题。为了更好地理解动态规划的思想,请看下面的例子。3.1递归法【例例3.13.1】斐波那契数列是数学中常见的数列,也叫兔子数列,它满足:a1=1,a2=1,an=an1an2(n2)。输入n,输出anmod 10000007 的值。(n100000)。输入样例:345输出样例:235【算法分析算法分析】看到题目以后,我们可以很轻松地写出两个版本的代码,一个是递推的代码,一个是

4、递归的代码。其中,递归的代码如下:/*prob:fib递归lang:c*/#includeiostream#define mod 100000073.1递归法using namespace std;int f(int x)if(x=2)return l;elsereturn(f(x1)+f(x2)%mod;int main()int n;while(cinn)coutf(n)endl;3.1递归法这一段代码看起来没有问题,但是运行起来却非常慢,当n=100的时候已经无法在有限的时间内得到结果。我们来看当n=5的例子,如图3.1所示。当n=5的时候,使用递归的思路计算,f(3)被重复地调用了2次

5、,f(2)被重复地调用了3次,而f(i)无论被调用多少次,它的返回值都是相同的。因此,进行了很多次无用的计算。如果能够在第一次计算f(i)的时候,把f(i)的结果记录下来,下一次调用的时候,直接返回f(i)的值,就可以避免很多次冗余的计算,如图3.2所示。这样一来,把冗余的计算都省去,程序的效率将得到质的提升。图图3.1 3.1 斐波那契数列的计算过程斐波那契数列的计算过程图图3.2 3.2 记忆化的优化效果,灰色部分不再被调用记忆化的优化效果,灰色部分不再被调用3.1递归法3.1递归法斐波那契数列的求法,从严格意义上来说,并不算是动态规划,但是却和动态规划有着千丝万缕的联系:(1)利用多余的

6、空间记录了重复状态的计算结果,避免了冗余的计算。(2)有状态转移方程(在这里是fi=fi1fi2,i3)。(3)任何一个状态只与确定的前几项直接相关。在这类问题中,保存已经求得的结果,在下次求解时直接调用结果,而不是重复地计算,这就是动态规划的基本思想。3.1递归法【3.1.23.1.2动态规划的概念动态规划的概念】动态规划是运筹学的一个分支。与其说动态规划是一种算法,不如说是一种思维方法来得更贴切。因为动态规划没有固定的框架,即便是应用到同一问题上,也可以建立多种形式的求解算法。许多隐式图上的算法,例如求单源最短路径的Dijkstra算法、广度优先搜索算法,都渗透着动态规划的思想。还有许多数

7、学问题,表面上看起来与动态规划风马牛不相及,但是其求解思想与动态规划是完全一致的。因此,动态规划不像深度或广度优先那样可以提供一套模式,需要的时候,取来就可以使用,它必须对具体问题进行具体分析处理,需要丰富的想象力去建立模型,需要创造性的思想去求解。前面我们曾说,动态规划不是万能的,它只适于解决满足一定条件的最优策略问题。“满足一定条件”主要指下面两点:(1)状态必须满足最优化原理;(2)状态必须满足无后效性。所谓的无后效性,是指过去的决策只能通过当前状态影响未来的发展,当前的状态是对以往决策的总结。这条特征说明什么呢?它说明动态规划适于解决当前决策和过去状态无关的问题。状态出现在策略的任何一

8、个位置,它的地位都是相同的,都可以实施同样的决策。这就是无后效性的内涵。3.1递归法【3.1.33.1.3动态规划的常用名词动态规划的常用名词】在学习动态规划之前,先得对下面的名词有所了解。本书将标准名词作了一些简化,便于大家更好地理解。(1)状态(state)。对于一个问题,所有可能到达的情况(包括初始情况和目标情况)都称为这个问题的一个状态。(2)状态变量(sk)。对每个状态 k 关联一个状态变量sk,它的值表示状态k所对应的问题的当前解值。(3)决策(decision)。决策是一种选择,于每一个状态而言,都可以选择某一种路线或方法,从而到达下一个状态。(4)决策变量(dk)。在状态 k

9、下的决策变量 dk的值表示对状态 k 当前所做出的决策。(5)策略。策略是一个决策的集合,在解决问题的时候,我们将一系列决策记录下来,就是一个策略,其中满足某些最优条件的策略称为最优策略。(6)状态转移函数(t)。从一个状态到另一个状态,可以依据一定的规则来前进。我们用一个函数t来描述这样的规则,它将状态i和决策变量di映射到另一个状态 j,记为t(i,di)=j。(7)状态转移方程(f)。状态转移方程f描述了状态变量之间的数学关系。一般来说,与最优化问题相对应,状态转移方程表示 si的值最优化的条件,或者说是状态i所对应问题的最优解值的计算公式,用代数式表示就是:si=f((sj,dj)|i

10、=t(j,dj),对决策变量dj所有可行的取值)3.1递归法【3.1.43.1.4动态规划算法的基本步骤动态规划算法的基本步骤】动态规划求解的基本步骤一般是:(1)定义状态;(2)划分阶段;(3)确定状态转移方程;(4)确定边界条件。【例例3.23.2】给定一个三角形,三角形的第一行有1个元素,第二行有2个元素第N行有N个元素,每个元素的值都是正数。现在从三角形的第1行1列出发,每次只能往下一行的同一列或者下一行的下一列走,走到最后一行停止,路径上能够取得的元素最大值之和是多少?例如:123456若路径为136,则能够去的的最大值和是13610。【算法分析算法分析】我们按照动态规划的基本求解步

11、骤来解这道题目。3.1递归法1.1.定义状态定义状态动态规划的状态定义方式和搜索算法的状态定义方式很相似。如果本题采用搜索算法来求解,则可以写出这样一个函数模型:dfs(int i,int j,int sum)表示现在走道(i,j)这个点,已经拿到的元素之和为sum。根据动态规划的思想,对于任何一个点(i,j),我们只关心能够走到这个点的最大的和sum。因此,动态规划的状态定义如下:dpij=sum,1i,jN,ji表示走到第i行j列的时候所能取得的最大元素之和。2.2.划分阶段划分阶段在本题中,无论每一步该怎么走,行数i必然1。因此,经过恰好N一1步后,将到达三角形的底部,寻找路径的过程结束

12、。所以,本题的阶段就是行走过程中的步数,与状态中的i一一对应。3.3.确定状态转移方程确定状态转移方程使用搜索算法求解此题,搜索的函数中会有如下代码dfs(i1,j,sumai1j);向下走dfs(i1,j1,sumai1j1);向右下走3.1递归法说明:每一个坐标(i,j)有两种决策,往下或者往右下走,换个角度思考,每一个坐标(i,j)也有两种到达方式:从上方来或者从左上方来。使用数学语言来描述这句话,就是状态转移方程:dpij=max(dpi-1j,dpi-1j-1)aij4.4.确定边界条件确定边界条件上述状态转移方程,由于是一步一步递推得到的,因此必须确定边界条件。边界条件包含两部分:

13、初始状态和边界状态。初始状态:dp11=a11所有的起点都是(1,1)边界状态dpij=0如果(i,j)不在三角形内此时,就可以编写代码了。【程序实现程序实现】/*prob:数字三角形lang:c*/#includeiostream#includestring.h#define mod 100000073.1递归法3.1递归法【3.2.13.2.1线性动态规划线性动态规划】【例例 3.33.3】魔族密码。题目描述:魔族现在使用一种新型的密码系统。每一个密码都是一个给定的仅包含小写字母的英文单词表,每个单词至少包含1个字母,至多75个字母。如果在一个由一个词或多个词组成的表中,除了最后一个以外,

14、每个单词都被其后的一个单词所包含,即前一个单词是后一个单词的前缀,则称词表为一个词链。例如下面的单词组成了一个词链:iint integer但下面的单词不组成词链:integer intern现在你要做的就是在一个给定的单词表中取出一些词,组成最长的词链,就是包含单词数最多的词链。将它的单词数统计出来,就得到密码了。输入格式:第一行为单词表中的单词数N(1N2000),下面每一行有一个单词,按字典顺序排列,中间也没有重复的单词。3.2动态规划的简单应用输入样例:5iintintegerinterninternet输出样例:4样例解释:i-int一intern-internet【算法分析算法分析

15、】这道题目就是一道典型的线性动态规划,为了更好地理解它,我们首先来看它的搜索实现。如果用搜索算法来写这道题目,写起来是非常容易的,代码如下:/*prob:vijosp1028dfslang:c*/3.2动态规划的简单应用#includeiostream#includestring.husing namespace std;int n;string s2005;int dp2005;int ans=0;bool can(int i,int j)if(i=0)return true;if(sj.find(si)=0)return true;return false;i指的是当前搜索到了哪一个字符串

16、step指的是当前一共接了几个字符串int dfs(int i,int step)ans=max(ans,step);3.2动态规划的简单应用 for(int j=i1;j=n;j)if(can(i,j)dfs(j,step1);return 0;int main()while(cinn)ans=0memset(dp,0,sizeof(dp);for(int i=1;i=n;i)cinsi;dfs(0,0);countansendl;3.2动态规划的简单应用再看这样一个例子:输人:i,it,in,int,inter,internet同样是inter结尾的单词序列,搜索的过程中会遇到以下几种可能

17、:i-interin-interint-interi-in-interi-int-interin-int-interi-in-int-inter它们都是以inter结尾的单词序列,之后能接的最长序列长度是一样的,假设从inter之后接出来的最长单词序列长度是x,而接到inter结尾的每一种情况的单词长度是ai的话,那么,每一种情况下的单词序列长度就是aix。对于本题,我们只关注最优解,而对于所有以inter结尾的单词序列,x都是相同的,那么,必然只有最大的ai才有可能产生最优解,而其他的状态都是无用的,我们不应该去计算它。因此,可以这样来定义一个状态:dpi表示以第i个单词结尾的单词序列的最长

18、长度。例如,dp5表示以inter结尾的单词序列中,最长的那一个序列的长度。显然,dp5=4。3.2动态规划的简单应用之后,我们来考虑任何两个状态之间的联系:对于一个dpi来说,哪些dpj和它相关呢?拿inter来举例子,以inter结尾的单词序列,只和i、in、int结尾的单词序列有关。更普遍的描述是:和i有关的每一个j满足:(1)Ji;(2)sj是si的前缀。当j可以接在i的前面时,dpjj和dpi的关系见表3.1。表表3.1 dpi3.1 dpi与与i i的关系的关系对于dp5,它与dp1、dp3、dp4都有联系,也就是说,以第5个字符串结尾的单词序列,可以由以第1、3、4个字符串结尾的

19、单词序列接上第五个单词,组成更长的一个单词序列,因此:dp5=dpi1i=1,3,4更普遍的:dpi=dpj+1ji且sj是si的前缀而我们只关心这其中最大的那一个,因此,需要在这个方程上做轻微的改动:dpi=max(dpi,dpj1)ji且sj是si的前缀3.2动态规划的简单应用还需要注意初始状态:dpi=1因为每一个字符串自己也是一个单词序列。有了初始状态、状态和状态转移方程,就是一个完整的动态规划模型了。【设计技巧】动态规划的常用实现方式有递推和递归两种,本题使用递推的方式实现起来更容易,效率更高。递推又分为顺推和逆推,本题将使用两种写法实现,方便读者更好地理解动态规划。【程序实现】/*

20、prob:vijos一p1028一逆推lang:c*/#includeiostream#includestring.husing namespace std;int n;string s2005;int dp2005;3.2动态规划的简单应用bool can(int i,int j)if(sj.find(si)=0)return true;return false;int main()while(cinn)memset(dp,0,sizeof(dp);for(inti=1;i=n;i)cinsi;int ans=0;对任何一个i,枚举所有可以接在它之前的字符串j for(int i=1;i=n

21、;i)dpi=1;for(int j=1;ji;j)3.2动态规划的简单应用3.2动态规划的简单应用3.2动态规划的简单应用3.2动态规划的简单应用两种实现方式略有不同,时间复杂度都是O(N2)这道题本质上是一类非常典型的线性动态规划算法:最长上升子序列。最长上升子序列的模型:一个数的序列bi,当b1b2bs时,我们称这个序列是上升的。对于给定的一个序列(a1,a2,an),我们可以得到一些上升的子序列(ai1,ai2,aik),这里1i1i2ikN。比如,对于序列(1,7,3,5,9,4,8),有它的一些上升子序列,如(1,7),(3,4,8)等等。这些子序列中最长的长度是4,比如子序列(1

22、,3,5,8)。我们的任务就是对于给定的序列,求出最长上升子序列的长度。最长上升子序列类型的题目,无论题目怎么变化,状态转移方程总是一样,只需要改变can()函数的写法,就可以适用。3.2动态规划的简单应用【3.2.23.2.2背包动态规划背包动态规划】背包型动态规划是一种典型的动态规划,它的基本模型是:有N件物品,每一件物品的价格是Vi,价值是Wi,现在有C元,问最多能买到价值多少的物品。背包问题可以延伸出很多变种:01背包、完全背包、多重背包、混合背包、二维费用背包等等,本节将挑选其中典型的问题进行分析。【例例3.63.6】01背包。有N件物品,每一件物品的价格是Vi,价值是Wi,现在有C

23、元,问最多能买到价值多少的物品。【算法分析算法分析】大多动态规划的题目,都很容易想到一个搜索的做法,此题也不例外。每一件物品只有取和不取两种情况,对于N件物品来说,最多只有2N种情况,递归穷举每一种情况,更新最优解即可,时间复杂度是O(2N)。/*prog:01背包dfslang:c*/#includeiostream#Includestring3.2动态规划的简单应用3.2动态规划的简单应用 while(cinnc)best=0;for(int i=1;i=n;i)cinviwi;dfs(1,0,0);coutbestendl;接下来,我们试着从搜索的过程中提取变化中的量。i:当前考虑到第i

24、个物品;V:当前的总价格;W:当前的总价值;其中,W 是要求的值。如此一来,状态定义就显而易见了:dpiV=W,表示当前考虑了前i个物品,一共买了价格为V的时候的最大总价值。那么,状态转移方程就很容易推出来了:dpiV=max(dpi1V,dpi1VviWi)其中,dpi1V表示没有选择第i个物品,dpi1V一vi表示选择了第i个物品。初始状态是 dp0O=0,表示一个物品都没考虑的时候,没有花一分钱,也没有拥有任何有价值的物品。3.2动态规划的简单应用3.2动态规划的简单应用3.2动态规划的简单应用【3.2.33.2.3区间动态规划区间动态规划】区间动态规划不同于线性动态规划,虽然它的模型也

25、是一条直线,但在动态规划的时候,却不能以一个点作为状态,必须以一个区间作为一个状态,其阶段也不像线性动态规划那样非常明显。因此,在代码的实现上区间动态规划往往使用的是记忆化搜索的形式。【例例 3.113.11】给定一个由字母组成的字符串,问最少需要多少代价才能够把它变成一个回文串。删除和添加字符都是有代价的,例如在输入样例中,添加一个“a”的代价是 1000,删除一个“a”的代价是1100。【算法分析算法分析】本题是 USACO 2007 Open Gold 的题目,是一道经典的区间动态规划题目。区间动态规划题目的特点是从两边到中间,我们先来从两边考虑这个题目。对于“abcb”最左边的字符“a

26、”,它要么跟结果最右边的字符“a”匹配,要么被删掉,那么,我们试着用 dp03来表示把“abcb”这个字符串变成合法回文串的最小代价。当“a”和最右边的“a”匹配时,最右边必须得添加一个字符“a”,因此 dp03可以由 dp13添加字符“a”的代价得到。当“a”被删掉时,dp03可以由 dp13删除字符“a”的代价得到。如果是“abca”这样的字符串,那么,“a”可以直接和最右边的“a”匹配,所以这种情况下,dp03还可以由 dp120(直接匹配没有代价)得到。3.2动态规划的简单应用因此,这道题目的状态转移方程是:dpi1j如果si=sjdpi1jinsert(si)添加一个字符在j右边dp

27、ij=min dpi1jdelete(si)删除第i个字符dpij1insert(sj)添加一个字符匹配sjdpij1delete(sj)删除第j个字符【设计技巧设计技巧】注意到删除一个字符和添加一个字符,本质上都是在给一个字符做匹配,因此,哪个代价小我们用哪一个,也就是说,对于任意一个字符,要么只删除,要么只插入。另外,dp1r有可能是0,因此我们必须用另一个数字来表示没有访问过此状态,1是一个不错的选择。【程序实现程序实现】/*prog:poj3280lang:c*/#includeiostream#includestring.h#includealgorithmusing namespa

28、ce std;3.2动态规划的简单应用3.2动态规划的简单应用3.2动态规划的简单应用【3.2.43.2.4网格动态规划网格动态规划】基于网格的动态规划模型往往是给定一个矩阵,然后让我们在这个矩阵上面寻找路径或是某种满足特定条件的图案。具体请看下面的例题。【例例3.133.13】盖房子。题目描述:永恒的灵魂最近得到了面积为nm的一大块土地,他想在这块土地上建造一所房子,这个房子必须是正方形的。但是,这块土地上面有很多不平坦的地方,他希望找到一块最大的正方形无瑕疵土地来盖房子。现在,您也来试试吧。输入格式:输入文件第一行为两个整数n,m(1n,m1000),接下来n行,每行有m个数字,数字之间用

29、空格隔开。0表示该块土地有瑕疵,1表示该块土地完好。输出格式:一个整数,最大正方形的边长。输入样例:440111111001101101输出样例:23.2动态规划的简单应用【算法分析算法分析】基于网格的动态规划,一般来说,核心思想就是找到点与点(有时候是线与线)之间的联系。对于这道题目,首先应当想到如何用一个点来表示一个正方形。由于正方形的长和宽是相等的,因此,知道这个正方形某一个顶点的坐标(x,y)以及它的边长len,就可以表示出来一个正方形。所以,我们的状态可以这么定义:dpxy=len。又因为对于同一个右下角的坐标(x,y),如果已经有了长度为len的正方形,那么以(x,y)为右下角的长

30、度为len-1、len-2、1的正方形对我们来说,已经没有任何意义。所以,我们的状态进一步可以表示为:dpxy=len,表示以(x,y)作为右下角的最大的正方形边长。接下来考虑这个状态是否可以进行转移,如果能够找到有效的状态转移方程,问题就迎刃而解了。当mapxy=0时,dpxy=0,因为这个坐标不能够成为正方形的一部分。当mapxy=1时,请看图3.4。图图3.4 dp3.4 dp值与相邻节点的关系值与相邻节点的关系3.2动态规划的简单应用图3.4中的三个正方形,表示对于右下角(x,y)这个坐标,(x一1,y)、(x,y一1)、(x-1,y-1)三个坐标作为右下角的最大正方形边长,记为dpx

31、-1y、dpxy-1、dpx一1y一1。则:图中非白色的部分都是1,和它们相邻的白色部分里面,至少有一个是0。否则,这三个正方形里面至少有一个边长还会更大一些。因此,dpxy最大一定不会超过它们三个中最小的边长1。进一步,可以推出dpxy=min(dpx1y,dpxy1,dpx-1y-1)1,证明过程如下:3.2动态规划的简单应用因此,状态转移方程是:dpxy=0if mapxy=0 dpxy=min(dpx1y,dpxy1,dpx1y1)1 if mapxy=1本题的状态数是 O(N2),转移的复杂度是 O(1),因此,算法的时间复杂度和空间复杂度都是 O(N2)。【程序实现程序实现】/*p

32、rog:vijos1057lang:c*/#includeiostream#includestring.h#includealgorithmusing namespace std;int dp10051005;int n,m;int arr10051005;int main()3.2动态规划的简单应用3.2动态规划的简单应用【3.3.13.3.1树形动态规划树形动态规划】树形动态规划区别于线性动态规划,题目往往给了一棵树,让我们在树上找到一些满足特定条件的事物。这一棵树可能给得很明显,也可能隐藏在题目描述里(例如依赖关系等),解题者遇到此类题目需要首先挖掘其树形结构的本质,然后再来思考算法解决

33、。【例例3.153.15】Anniversary party。给定一棵树,每个节点有一个权值,现在要在树上选择一些点,只有一个限制条件,就是任何一对父亲和儿子之中最多只能选一个,问最大的权值是多少。【算法分析算法分析】对于树形动态规划,很重要的一点是找到父亲和儿子之间的联系,而这个联系,要通过状态和状态之间的转移来体现。所以,首先来考虑如何定义状态。树形dp不像是简单线性dp,状态一目了然,树形dp的定义大多从题目要求的结果开始。本题要求的结果是,“以1为根的子树(包括1本身),父亲和儿子最多只能选一个,最大的权值”。这里面,出现的唯一一个可以表示状态的数字就是节点编号,因此,我们首先可以尝试

34、这样定义状态:dpi:表示i以及i的子树,在满足题目要求的情况下最大的权值。那么dp1就是题目所求。但是,当考虑到状态转移时,仅仅使用一个i却无法让我们把i和它的每一个儿子j联系到一起,因为并不知道它的儿子j到底可以选还是不能选。所以,还需要另一维状态(0,1)来表示这个节点是选了还是没有选,最终,状态定义如下:dpi0表示i为根,且i不选的最大权值。dpi1表示i为根,且i选了的最大权值。3.3动态规划的深入研究当i没有被选择的时候,对于它的每一个儿子j,他们可以选也可以不选,因此:dpi0=max(dpj0,dpj1)j是i的每一个儿子当i被选择了的时候,那么它的每一个儿子j必然不能被选择

35、,因此:dpi1=dpj0j是i的每一个儿子最后的答案就是max(dp10,dp11)。实现本题目需要对整棵树进行遍历,因而时间复杂度是O(N)的,空间复杂度也是O(N)。【设计技巧设计技巧】使用vector对树中的每一个边进行存储,这样的存储方法比邻接表节省空间。【程序实现程序实现】/*prog:vijos1493lang:c*/#includecstdio#includecstring#includealgorithm#includeiostream#includevectorusing namespace std;3.3动态规划的深入研究3.3动态规划的深入研究3.3动态规划的深入研究3

36、.3动态规划的深入研究【3.3.23.3.2状态压缩动态规划状态压缩动态规划】状态压缩动态规划(简称状压dp)是另一类非常典型的动态规划,通常使用在NP问题的小规模求解中。虽然该算法具有指数级别的复杂度,但速度比搜索快,其思想非常值得借鉴。为了更好地理解状压dp,首先介绍位运算相关的知识。(1)“”符号:xy,会将两个十进制数在二进制下进行与运算,然后返回其十进制下的值。例如3(11)2(10)=2(10)。(2)“|”符号:x|y,会将两个十进制数在二进制下进行或运算,然后返回其十进制下的值。例如3(1l)|l2(10)=3(11)。(3)“”符号xy,会将两个十进制数在二进制下进行异或运算

37、,然后返回其十进制下的值。例如3(11)2(10)=1(01)。(4)“”符号左移操作。例如,x2,将x在二进制下的每一位向左移动两位,最右边用0填充,相当于让x乘以4。相应的,“”是右移操作,x1相当于x/2,去掉x二进制下的最右一位。这四种运算在状压dp中有着广泛的应用,常见的应用如下:3.3动态规划的深入研究(1)判断一个数字x二进制下第i位是不是等于1。方法:if(1(i)x)0)将1左移i位,相当于制造了一个只有第i位上是1,其他位上都是0的二进制数。然后与x做与运算,如果结果0,说明x第i位上是1,反之则是0。(2)将一个数字x二进制下第i位更改成1。方法:x=x|1(i)证明方法

38、与(1)类似,此处不再重复证明。(3)把一个数字二进制下最靠右的第一个1去掉。方法:x=x(x1)3.3动态规划的深入研究【3.3.33.3.3基于连通性的状态压缩动态规划基于连通性的状态压缩动态规划】基于连通性的状态压缩动态规划是一类很典型的状态压缩动态规划问题,因为其压缩的本质并不像普通的状态压缩动态规划那样用0或者1来表示未使用、使用两种状态,而是使用数字来表示类似插头的状态,因此,它又被称作插头DP。插头DP本质上是一类状态压缩DP,因此,依然避免不了其指数级别的算法复杂度,即便如此,它依然要比普通的搜索算法快很多。【例例3.203.20】Postal Vans。题目描述有一个4n的矩

39、阵,从左上角出发,每次可以向四个方向走一步,求经过每个格子恰好一次,再回到起点的走法数。【算法分析算法分析】看到此题,许多读者觉得4很小,会想到搜索算法或者是递推公式,而实际上,搜索算法是不能解决此题的,当n稍大一点,搜索算法即使写得再漂亮,也不能通过此题全部测试数据。本题确实有递推公式,但递推公式却不是那么好找,因此,可以考虑使用插头DP。为了更好地了解插头DP,首先引入以下几个概念:(1)插头。对于矩阵上的任何一个格点,路径总是会穿过它,也就是从一头进入,从一头出去,这样的情况一共有6种,如图3.10所示。图图3.10 3.10 经过一个格点的六种形式经过一个格点的六种形式3.3动态规划的

40、深入研究一个合法的路径需要满足的必要条件之一是:它的每一个格子上的路径插头都是上述六者之一,并且要相互匹配。相互匹配的意思是,如果一个格子上方的格子有向下的插头,那么这个格子就必须有向上的插头与它相匹配。(2)轮廓线。对于任何一个未决策的格子,仅有其上边和左边的格子对其放置方法有影响,因此,可以根据当前已经决策的格子画出一条轮廓线,分割出已经决策和未决策的格子。如图3.11所示就是两种典型的轮廓线,一种是基于格子的轮廓线,当前该转移的是轮廓线拐角处的格子;一种是基于行的轮廓线,当前该转移的是轮廓线下方的一整行。图图3.11 3.11 两种轮廓线划分方式两种轮廓线划分方式3.3动态规划的深入研究

41、对于第一种情况,涉及的插头一共有N1个,其中N个下插头,1个右插头,需要保存的插头数量是N1个;对于第二种情况,只有N个下插头,需要保存的插头数是N个。(3)连通性。对于这类动态规划问题,除了要保存每一个插头外,还需要记录这些插头的连通性情况。例如,使用(1,2)(3,4)来表示该行第1、2个格子已经连通,第3、4个格子已经连通。如图3.12所示,两者的下插头完全一致,但连通性却完全不同。因此,还需要在状态中表示它们的连通性。图图3.123.12两种插头完全一致,但连通性完全不同的情况两种插头完全一致,但连通性完全不同的情况3.3动态规划的深入研究由于插头的表示已经是指数级别的空间,表示连通性

42、如果再需要指数型的空间,那么空间和时间的消耗将是巨大的。因此,需要有更好的办法去表示连通性,通用的一个办法称作“括号表示法”。对于同一行的四个格子,假设它们都有下插头,则它们的连通性只可能有两种情况:(1,2),(3,4),(1,4),(2,3),而不可能是(1,3),(2,4)。更普遍的,因为插头永远都不可能有交叉,所以任何两个格子之间的连通性也不会存在交叉。这和括号匹配是完全一致的。括号表示法的基本思想是三进制:0:无插头状态,用#表示。1:左括号插头,用(表示。2:右括号插头,用)表示。如图3.13左图所示,(使用格点转移)可以表示为:()#)。如图3.13右图所示,(使用行来转移)可以

43、表示为()()。图图3.13 3.13 两种不同的状态表示方式两种不同的状态表示方式3.3动态规划的深入研究1.1.基于格点的状态转移基于格点的状态转移基于格点的状态转移方式每次仅转移一个格子,转移的时候需要考虑这个格子的左方以及上方是否有插头。(1)左方无插头,上方无插头。*#*(*代表#、(、)中任意一个),只能在此处加入一个 形插头,状态变为*()*。(2)左方和上方只有一个插头。此时,该格必然有一个插头和这个插头匹配,另一个插头插向下方或右方。这个格子相当于延续了之前该插头的连通状态,因此,转移方式是:将插头所在的位不动(插头插向下方)或向右移动一位(插向右方)。(3)左方有插头,上方

44、有插头。这种情况下相当于合并了两个连通块,需要考虑两个插头的括号表示情况:casel:“(”,两者都是左插头,此刻要把两个连通分量合并,就必须修改它们对应的右括号,使得它们对应的右括号匹配起来,例如:#(#),将两个“(”合并时,需要修改第二个“(”所匹配的“)”为“(”,使它们继续匹配,变为#井#井#()。case2:“)”,两者都是右插头,此时,合并它们相当于直接把它们变为“#”,再将第二个“)”对应的左插头“(”改为右插头“)”。case3:“()”,即两者本来就是匹配的。此时,合并它们相当于把它们连起来形成回路。对于需要形成一条回路的题目,只有在最后一个格子形成回路才是合法的转移方式。

45、其中,左方有插头、上方无插头以及左方无插头、上方有插头的情况十分类似,在实现代码的时候可以合并。3.3动态规划的深入研究2.2.基于行的状态转移基于行的状态转移基于行的状态转移,使用搜索的办法实现,dfs每一行的合法状态,然后更新状态至下一行。这种情况容易实现但算法复杂度较高,很少有人使用。在此不再赘述。【设计技巧设计技巧】需要注意的是,虽然插头DP使用的是三进制的状态表示,但是往往在实现的时候使用的却是四进制(仅使用“0”、“1”、“2”,“3”不表示任何意义)。原因是由于计算机本身的设计导致其对2的幂次方进制数计算速度很快,使用四进制可以利用到位运算,加快运行速度。此外,由于在状态转移的过

46、程中,需要知道每一个左括号所对应的右括号的位置,由于合法状态是很有限的,因此,可以通过预处理的方式将合法状态以及这些状态下每一个左括号所匹配的右括号的位置记录下来,利用额外空间换来时间复杂度的下降。【设计技巧设计技巧】在实现代码时,使用一个int类型来保存每一个状态,括号在四进制中从低位到高位依次表示从左到右的每一个括号,例如9(21)3实际上代表了一个从左到右的匹配的括号(),请读者在阅读代码的时候注意。本题在读入数据过大的时候会超过longlong类型,因此需要用到高精度运算,实现时可以用结构体复写加法运算符的方式来实现。熟悉Java的读者也可以直接使用Java中的BigInteger类。

47、【程序实现】/*prog:USACO6.6.1lang:c3.3动态规划的深入研究3.3动态规划的深入研究3.3动态规划的深入研究3.3动态规划的深入研究3.3动态规划的深入研究3.3动态规划的深入研究3.3动态规划的深入研究3.3动态规划的深入研究本题稍作变化,就可以给出很多道插头DP类的题目:例如,从(1,1)点出发,回到(1,n)点;找出若干个环把图上所有点都覆盖;从(1,1)点出发,把所有点恰好都走一次,但不需要回到(1,1)点等。(1)从(1,1)点出发,回到(1,n)点。我们可以认为这个迷宫是从(0,1)点进入,最后回到(0,n)点,这样一来,我们只需要在转移状态的时候,强制要求(

48、1,1)点和(1,n)点必须得有上插头,其他不变即可。(2)找出若干个环把图上所有点都覆盖。在原来的题目中,必须只有一个环。因此,只有在最后一个点(n,n)的时候,才处理了同时有左上插头并且它们是()的情况,在本问题里,只需要把这个特殊要求去掉即可,即在任何一个点,只要满足左上都有插头且它们是(),就把它们合并。(3)从(1,1)点出发,把所有点恰好都走一次,但不需要回到(1,1)点。此时,人为地给(1,1)点添加一个上插头,使得(1,1)点只可能被经过一次,之后在状态中多加入一维状态0 or 1用来表示当前是否已经把某一个点作为终点,当这一维状态是0的时候,可以在某一个格子转移的时候只给它添

49、加一个插头,然后把0修改成1,最后到(n,n)点要求必须是()才更新答案即可;如果到达最后一个节点(n,n)时这一维状态依然是0,则在此处考虑两种插头的插法,并更新答案。3.3动态规划的深入研究【3.3.43.3.4数位计数类动态规划数位计数类动态规划】数位计数问题是一类比较麻烦的问题,这类问题难度往往不大,但是总是有各种特殊情况,此类题目很多时候有巧解,但做法并不能推广。因此,需要学习数位计数类动态规划。【例例 3.223.22】给定范围 A、B(1A,B1012),求A,B内所有数的五进制下各个数位之和。【算法分析算法分析】算法1:数位统计类的题目,在数据量并不大(例如本题)的情况下,是可

50、以有分段求解的做法的。可以分段求解建立在数据范围并不是特别大,使用暴力思路可以在几十分钟以内就能求得结果的前提下。分段求解的思路类似于打表,但本题的数据量太大,无法把所有的答案求出然后记录下来。因此,可以使用分段求解的思路,分段求解的基本思想如下:把数据每k 长分成一段(此处选择为 106),使用 ai记录每一段的结果之和。例如,a0表示1,106所有数二进制下各个数位之和。暴力求解后打表保存在代码里,当询问一个区间A,B时,可以把A,B拆分成若干个区间A,l1、l11,l1106-1,lx、1,B。这些区间,除了第一个与最后一个外,都在已经保存的答案数组中,可以直接输出答案,而剩下的两个区间

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 大学
版权提示 | 免责声明

1,本文(《算法分析与设计技巧》课件第三章.pptx)为本站会员(momomo)主动上传,163文库仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。
2,用户下载本文档,所消耗的文币(积分)将全额增加到上传者的账号。
3, 若此文所含内容侵犯了您的版权或隐私,请立即通知163文库(发送邮件至3464097650@qq.com或直接QQ联系客服),我们立即给予删除!


侵权处理QQ:3464097650--上传资料QQ:3464097650

【声明】本站为“文档C2C交易模式”,即用户上传的文档直接卖给(下载)用户,本站只是网络空间服务平台,本站所有原创文档下载所得归上传人所有,如您发现上传作品侵犯了您的版权,请立刻联系我们并提供证据,我们将在3个工作日内予以改正。


163文库-Www.163Wenku.Com |网站地图|