动态规划之状态压缩

动态规划之状态压缩
动态规划之状态压缩

状态压缩

Abstract

信息学发展势头迅猛,信息学奥赛的题目来源遍及各行各业,经常有一些在实际应用中很有价值的问题被引入信息学并得到有效解决。然而有一些问题却被认为很可能不存在有效的(多项式级的)算法,本文以对几个例题的剖析,简述状态压缩思想及其应用。

Keywords

状态压缩、Hash、动态规划、递推

Content

Introducti o n

作为OIers,我们不同程度地知道各式各样的算法。这些算法有的以O(logn)的复杂度运行,如二分查找、欧几里德GCD算法(连续两次迭代后的余数至多为

原数的一半)、平衡树,有的以)运行,例如二级索引、块状链表,再往上有O(n)、O(n p log q n)……大部分问题的算法都有一个多项式级别的时间复杂度上界1,我们一般称这类问题2为P (deterministic Polynomial-time)类问题,例如在有向图中求最短路径。然而存在几类问题,至今仍未被很好地解决,人们怀疑他们根本没有多项式时间复杂度的算法,NPC(NP-Complete)和NPH(NP-Hard)就是其中的两类,例如问一个图是否存在哈密顿圈(NPC)、问一个图是否不存在哈密顿圈(NPH)、求一个完全图中最短的哈密顿圈(即经典的Traveling Salesman Problem货郎担问题,NPH)、在有向图中求最长(简单)路径(NPH),对这些问题尚不知有多项式时间的算法存在。P和NPC都是NP(Non-deterministic Polynomial-time)的子集,NPC则代表了NP类中最难的一类问题,所有的NP类问题都可以在多项式时间内归约到NPC问题中去。NPH包含了NPC和其他一些不属于NP(也更难)的问题,NPC问题的函数版本(相对于判定性版本)一般是NPH的,例如问一个图是否存在哈密顿圈是NPC的,但求最短的哈密顿圈则是NPH的,原因在于我们可以在多项式时间内验证一个回路是否真的是哈密顿回路,却无法在多项式时间内验证其是否是最短的,NP类要求能在多项式时间内验证问题的一个解是否真的是一个解,所以最优化TSP问题不是NP的,而是NPH的。存在判定性TSP问题,它要求判定给定的完全图是否存在权和小于某常数v的哈密顿圈,这个问题的解显然可以在多项式时间内验证,因此它是NP

1请注意,大O符号表示上界,即O(n)的算法可以被认为是O(n2)的,O(n p log q n)可以被认为是O(n p+1)的。2在更正式的定义中,下面提到的概念都只对判定性问题或问题的判定版本才存在(NPH除外)。Levin给出了一个适用于非判定问题的更一般的概念,但他的论文比Cook的晚发表2年。

的,更精确地说是NPC的。1

如上所述,对于NPC和NPH问题,至今尚未找到多项式时间复杂度的算法。然而它们的应用又是如此的广泛,我们不得不努力寻找好的解决方案。毫无疑问,对于这些问题,使用暴力的搜索是可以得到正确的答案的,但在信息学竞赛那有限的时间内,很难写出速度可以忍受的暴力搜索。例如对于TSP问题,暴力搜索的复杂度是O(n!),如此高的复杂度使得它对于高于10的数据规模就无能为力了。那么,有没有一种算法,它可以在很短的时间内实现,而其最坏情况下的表现比搜索好呢?答案是肯定的——状态压缩(States Compression,SC)。

作为对下文的准备,这里先为使用Pascal的OIers简要介绍一下C/C++样式的位运算(bitwise operation)。

一、基本运算符

名称 C/C++样式 Pascal样式简记法则

按位与(bitwise AND) & and 全一则一

否则为零

按位或(bitwise OR) | or 有一则一

否则为零

按位取反(bitwise NOT) ~ not 是零则一

是一则零

按位异或(bitwise XOR) ^ xor 不同则一

相同则零

以上各运算符的优先级从高到低依次为:~,&,^,|

二、特殊应用

a)and:

i.用以取出一个数的某些二进制位

ii.取出一个数二进制中的最后一个1(lowbit)2:x&-x

b)or:用以将一个数的某些位设为1

c)not:用以间接构造一些数:~0u=4294967295=232-1

d)xor:

i.不使用中间变量交换两个数:a=a^b;b=a^b;a=a^b;

ii.将一个数的某些位取反

有了这些基础,就可以开始了。

1不应该混淆P、NP、NPC、NPH的概念。这里只是粗略介绍,详见关于算法分析的书籍(实际上一篇paper 根本讲不完,因为还有co-NP、co-NPC、#P、#PC……后文会给出一个#PC的例子),这会使新手读者的理论水平上一个台阶。弄不明白也没关系,基本不影响对本文其他部分的理解^_^

2具有同样作用的还有(x-1)&x^x,这个的道理更容易理解。lowbit在树状数组(某种数据结构)中可以用到,这里不再单独介绍,感兴趣的可以参阅各牛的论文,或者加我QQ。建议掌握,否则可能会看不懂我的部分代码。

Getting Started

我们暂时避开状态压缩的定义,先来看一个小小的例题。

【引例】1

在n*n(n≤20)的方格棋盘上放置n 个车(可以攻击所在行、列),求使它们不能互相攻击的方案总数。

【分析】

这个题目之所以是作为引例而不是例题,是因为它实在是个非常简单的组合学问题:我们一行一行放置,则第一行有n 种选择,第二行n -1,……,最后一行只有1种选择,根据乘法原理,答案就是n!。这里既然以它作为状态压缩的引例,当然不会是为了介绍组合数学。我们下面来看另外一种解法:状态压缩递推(States Compressing Recursion,SCR)。

我们仍然一行一行放置。取棋子的放置情况作为状态,某一列如果已经放置棋子则为1,否则为0。这样,一个状态就可以用一个最多20位的二进制数2表示。例如n=5,第1、3、4列已经放置,则这个状态可以表示为01101(从右到左)。设f s 为达到状态s 的方案数,则可以尝试建立f 的递推关系。

考虑n=5,s=01101。这个状态是怎么得到的呢?因为我们是一行一行放置的,所以当达到s 时已经放到了第三行。又因为一行能且仅能放置一个车,所以我们知道状态s 一定来自:

①前两行在第3、4列放置了棋子(不考虑顺序,下同),第三行在第1列放置;②前两行在第1、4列放置了棋子,第三行在第3列放置;

③前两行在第1、3列放置了棋子,第三行在第4列放置。

上图依次为①②③这三种情况,(红,蓝),(红,绿)分别代表两种顺序。这三种情况

互不相交,且只可能有这三种情况,根据加法原理,f s 应该等于这三种情况的和。写成递推式就是:

01101011000100100101f f f f =++

根据上面的讨论思路推广之,得到引例的解决办法:

021

?==∑i s s f f f

其中s 的右起第i +1位为1 (其实就是在枚举s 的二进制表示中的1) 3。

反思这个算法,其正确性毋庸置疑(可以和n!对比验证)。但是算法的时间复杂度为O(n2n ),空间复杂度O(2n ),是个指数级的算法,比循环计算n!差了好多,它有什么优势?较大的推广空间。4 1

本文所有例题的C++代码均可以在附件中找到。 2 方便、整齐起见,本文中不在数的后面标明进制。

3 考虑上节介绍的位运算的特殊应用,可以更精巧地实现。

4 还有一个很…的用处,即对新手说:“来看看我这个计算n!的程序,连这都看不懂就别OI 了~”

Sample Problems

【例1】1

在n*n(n≤20)的方格棋盘上放置n 个车,某些格子不能放,求使它们不能互相攻击的方案总数。

【分析】

对于这个题目,如果组合数学学得不够扎实,是否还能一眼看出解法?应该很难。对于这个题目,确实存在数学方法(容斥原理),但因为和引例同样的理由,这里不再赘述。

联系引例的思路,发现我们并不需要对算法进行太大的改变。引例的算法是在枚举当前行(即s 中1的个数,设为r )的放置位置(即枚举每个1),而对于例1,第r 行可能存在无法放置的格子,怎么解决这个问题呢?枚举1的时候判断一下嘛!事实的确是这样,枚举1的时候判断一下是否是不允许放置的格子即可。 但是对于n=20,O(n2n )的复杂度已经不允许我们再进行多余的判断。所以实现这个算法时应该应用一些技巧。对于第r 行,我们用a r 表示不允许放置的情况,如果第r 行某一位不允许放置则此位为0,否则为1,这可以在读入数据阶段完成。运算时,对于状态s ,用tmps=s&a r 来代替s 进行枚举,即不枚举s 中的1转而枚举tmps 中的1。因为tmps 保证了不允许放置的位为0,这样就可以不用多余的判断来实现算法,代码中只增加了计算a 数组和r 的部分,而时间复杂度没有太大变化。

这样,我们直接套用引例的算法就使得看上去更难的例1得到了解决。读者可能会说,这题用容斥原理更快。没错,的确是这样。但是,容斥原理在这题上只有当棋盘为正方形、放入的棋子个数为n 、且棋盘上禁止放置的格子较少时才有简单的形式和较快的速度。如果再对例1进行推广,要在m*n 的棋盘上放置k 个车,那么容斥原理是无能为力的,而SCR 算法只要进行很少的改变就可以解决问题2。这也体现出了引例中给出的算法具有很大的扩展潜力。

棋盘模型是状态压缩最好的展示舞台之一。下面再看几个和棋盘有关的题目。

【例2】3

给出一个n*m 的棋盘(n 、m≤80,n*m ≤80),要在棋盘上放k(k ≤20)个棋子,使得任意两个棋子不相邻。每次试验随机分配一种方案,求第一次出现合法方案时试验的期望次数,答案用既约分数表示。

【分析】

显然,本题中的期望次数应该为出现合法方案的概率的倒数,则问题转化为

求出现合法方案的概率。而概率=方案总数

合法方案数,方案总数显然为C(n*m,k),则问题转化为求合法方案数。整理一下,现在的问题是:在n*m 的棋盘上放k 个棋子,求使得任意两个棋子不相邻的放置方案数。 1

题目来源:经典组合学问题。 2 如果这样改编,则状态的维数要增加,如有疑问可以参考下面的几个例子,这里不再赘述。

3 题目来源:经典问题改编。

这个题目的状态压缩模型是比较隐蔽的。观察题目给出的规模,n 、m≤80,这个规模要想用SC 是困难的,若同样用上例的状态表示方法(放则为1,不放为0),280无论在时间还是在空间上都无法承受。然而我们还看到n*m ≤80,这种给出数据规模的方法是不多见的,有什么玄机呢?能把状态数控制在可以承受的范围吗?稍微一思考,我们可以发现:9*9=81>80,即如果n,m 都大于等于9,将不再满足n*m ≤80这一条件。所以,我们有n 或m 小于等于8,而28是可以承受的。我们假设m ≤n (否则交换,由对称性知结果不变)n 是行数m 是列数,则每行的状态可以用m 位的二进制数表示。但是本题和例1又有不同:例1每行每列都只能放置一个棋子,而本题却只限制每行每列的棋子不相邻。但是,上例中枚举当前行的放置方案的做法依然可行。我们用数组s 保存一行中所有的num 个放置方案1,则s 数组可以在预处理过程中用DFS 求出,同时用c i 保存第i 个状态中1的个数以避免重复计算。开始设计状态。如例1的注释所说,维数需要增加,原因在于并不是每一行只放一个棋子,也不是每一行都要求有棋子,原先的表示方法已经无法完整表达一个状态。我们用f i,j,k 表示第i 行的状态为s j 且前i 行已经放置了k 个棋子2的方案数。沿用枚举当前行方案的做法,只要当前行的方案和上一行的方案不冲突即可,“微观”地讲,即s sid(i)和s sid(i -1)没有同为1的位,其中sid(x)表示第x 行的状态的编号。然而,虽然我们枚举了第i 行的放置方案,但却不知道其上一行(第i -1行)的方案。为了解决这个问题,我们不得不连第i -1行的状态一起枚举,则可以写出递推式:

0,1,0,,1,,1

??==∑j i j k i p k c f f f (要求s j 与s p 不冲突)

其中s 1=0,即在当前行不放置棋子;j 和p 是需要枚举的两个状态编号。3

有了合法方案数,剩下的问题就不是很困难了,需要注意的就只有C(n*m,k)可能超出64位整数范围的问题,这可以通过边计算边用GCD 约分来解决,具体可以参考附件中的代码。当然,实现上仍有少许优化空间,例如第i 行只和第i-1行有关,可以用滚动数组节省空间。这个算法时间复杂度O(n*pn*num 2),空间复杂度(滚动数组)O(pn*num),对于题目给定的规模是可以很快出解的。

通过上文的例题,读者应该已经对状态压缩有了一些感性的认识。下面这个题目可以作为练习。

【例3】4

在n*n(n ≤10)的棋盘上放k 个国王(可攻击相邻的8个格子),求使它们无法互相攻击的方案数。

1 运用简单的组合数学知识可以求出:在格数为m 的一行上放棋子且相邻两个棋子中间的空格不能少于d 的2((1),)0

m C m r d r r num ??==,

其中m/2为上取整。对于本题,num=144。此式在下文也有应用。 2

为了避免歧义,下文中用pn 代替原题中的k 。 3 请读者停下来仔细揣摩这个递推式,否则可能影响对本文后面内容的理解!

4 题目来源:SGU.223《Little Kings 》

【分析】

其实有了前面几个例子的分析,这个题目应该是可以独立解决的。不过既然确实有疑问,那我们就来分析一下。

首先,读者应该能想到将一行的状态DFS 出来(如果不能,请返回重新阅读,谢谢),仍然设为数组s ,同时仍然设有数组c 记录状态对应的1的个数。和例2相同,仍然以f i,j,k 表示第i 行状态为s j ,且前i 行已经放置了k 个棋子的方案数。递推式仍然可以写作:

0,1,0,,1,,1

??==∑j i j k i p k c f f f (要求s j 与s p 不冲突)

可是问题出来了:这题不但要求不能行、列相邻,甚至不能对角线相邻!s j 与s p 不冲突怎么“微观地”表示呢?其实,联系例1对无法放置棋子的格子的处理,可以尝试用预处理解决这个问题。数组q 保存在当前行的状态为s 的情况下上一行不允许放置的情况(不允许放置为1,否则为0),则q 数组可以在DFS 计算s 的时候顺便算出。这样,可以用q j 代替s j 去和s p 进行“微观的”比较,得出条件是:q j &s p =0。1

解决掉这唯一的问题,接下来的工作就没有什么难度了。算法复杂度同例2。

下一个例题是状态压缩棋盘模型的经典题目,希望解决这个经典的题目能够锻炼读者的熟练度。

【例4】2

给出一个n*m(n ≤100,m ≤10)的棋盘,一些格子不能放置棋子。求最多能在棋盘上放置多少个棋子,使得每一行每一列的任两个棋子间至少有两个空格。

【分析】3

显然,读者应该已经有DFS 搜出一行可能状态的意识了(否则请重新阅读之前的内容3遍,谢谢),依然设为s ,依旧有c 保存s 中1的个数,依照例1的预处理搞定不能放置棋子的格子。

问题是,这个题目的状态怎么选?继续像例2、3那样似乎不行,原因在于棋子的攻击范围加大了。但是我们照葫芦画瓢:例2、3的攻击范围只有一格,所以我们的状态中只需要有当前行的状态即可进行递推,而本题攻击范围是两格,因此增加一维来表示上一行的状态。用f i,j,k 表示第i 行状态为s j 、第i-1行状态为s k 时前i 行至多能放置的棋子数,则状态转移方程很容易写出:

,,1,,max{}i j k i k l j f f c ?=+ (要求s j , s k , s l 互不冲突) 因为棋子攻击范围为两格,可以直观地想像到num 不会很大。的确,由例2中得到的num 的计算式并代入d=2、m=10,得到num=60。显然算法时间复杂度为O(n*num 3),空间复杂度(滚动数组)O(num 2)。此算法还有优化空间。我们分别枚举了三行的状态,还需要对这三个状态进行是否冲突的判断,这势必会重复枚举到一些冲突的状态组合。我们可以在计算出s 后算出哪些状态可以分别作为两行的状态,这样在DP 时就不需要进行盲目的枚举。这样修改后的算法理论上比上述算法更优,但因为num 本身很小,所以这样修改没有显著地减少运行时 1

聪明的读者应该能想出不用q 数组的方法(提示:位运算)。 2 题目来源:NOI2001《炮兵阵地》;PKU.1185

3 读者应该独立思考一个小时后再看分析,它值得这样。

间。值得一提的是,本题笔者的算法虽然在理论上并不是最优1,但由于位运算的使用,截至07年6月,笔者的程序在PKU OJ 上长度最短,速度第二快。 这个题目是国内比赛中较早出现的状态压缩题。它告诉我们状态压缩不仅可以像前几个例题那样求方案数,而且可以求最优方案,即状态压缩思想既可以应用到递推上(SCR ),又可以应用到DP 上(SCDP ),更说明其有广泛的应用空间。

看了这么多棋盘模型应用状态压缩的实例,读者可能会有疑问,难道状态压缩只在棋盘上放棋子的题目中有用?不是的。我们暂时转移视线,来看看状态压缩在其他地方的应用——覆盖模型。

【例5】2

给出n*m(1≤n 、

m ≤11)的方格棋盘,用1*2的长方形骨牌不重叠地覆盖这个棋盘,求覆盖满的方案数。

【分析】

这也是个经典的组合数学问题:多米诺骨牌完美覆盖问题(或所谓二聚物问题)。有很多关于这个问题的结论,甚至还有个专门的公式:如果m 、n 中至少有

一个是偶数,则结果=∏∏==??????+22

11)1+n *j (cos 4)1+m *i (4cos 22m n i j ππ。这个公式形式比较简单,且计算的复杂度是O(

4

*n m )的,很高效。但是这个公式内还有三角函数,对于大的n ,m 无法实现仅算出答案模某个数的余数,且中学生几乎不可能理解,所以对我们能力的提高没有任何帮助。用SCR 算法能较好地解决这个问题。

显然,如果n 、m 都是奇数则无解(由棋盘面积的奇偶性知),否则必然有至少一个解(很容易构造出),所以假设n 、m 至少有一个偶数,且m ≤n (否则交换)。我们依然像前面的例题一样把每行的放置方案DFS 出来,逐行计算。用f i,s 表示

把前i-1行覆盖满、

第i 行覆盖状态为s 的覆盖方案数。因为在第i 行上放置的骨牌最多也只能影响到第i-1行,则容易得递推式: 0,21,11,21

m i s i s f f f ??==∑ (s 1,s2)整体作为放置方案

可以把所有方案DFS 预处理出来。下面讨论一下本题的一些细节。

首先讨论DFS 的一些细节。对于当前行每一个位置,我们有3种放置方法:①竖直覆盖,占据当前格和上一行同一列的格;②水平覆盖,占据当前格和该行下一格;③不放置骨牌,直接空格。如何根据这些枚举出每个(s 1,s2)呢?下面介绍两种方法:

第一种:

DFS 共5个参数,分别为:p (当前列号),s 1、s2(当前行和上一行的覆盖

情况),b 1、b2(上一列的放置对当前列两行的影响,影响为1否则为0)。初 1 有种应用三进制的方法理论上可能更优,但因为手工转换进制效率远不如系统的位运算高,且无法使用位运算判断可行性,程序效率并不比文中的高,故不再赘述那种方法。下文的一些题目将用到多进制。 2 题目来源:经典问题;PKU.2411《Mondriaan's Dream 》

始时s 1=s2=b 1=b2=0。①p=p +1,s 1=s 1*2+1,s2=s2*21,b 1=b2=0;②p=p +1,s 1=s 1*2+1,s2=s2*2+1,b 1=1,b2=0;③p=p +1,s 1=s 1*2,s2=s2*2+1,b 1=b2=0。当p 移出边界且b 1=b2=0时记录此方案。

第二种:

观察第一种方法,发现b2始终为0,知这种方法有一定的冗余。换个更

自然的方法,去掉参数b 1、b2。①p=p +1,s 1=s 1*2+1,s2=s2*2;②p=p+2,s 1=s 1*4+3,s2=s2*4+3;③p=p +1,s 1=s 1*2,s2=s2*2+1。当p 移出边界时记录此方案。这样,我们通过改变p 的移动距离成功简化了DFS 过程,而且这种方法更加自然。

DFS 过程有了,实现方法却还有值得讨论的地方。前面的例题中,我们为什么总是把放置方案DFS 预处理保存起来?是因为不合法的状态太多,每次都重新DFS 太浪费时间。然而回到这个题目,特别是当采用第二种时,我们的DFS 过程中甚至只有一个判断(递归边界),说明根本没有多少不合法的方案,也就没有必要把所有方案保存下来,对于每行都重新DFS 即可,这不会增加运行时间却可以节省一些内存。

这个算法时间复杂度为多少呢?因为DFS 时以两行为对象,每行2m ,共进行n 次DFS ,所以是O(n*4m )?根据“O ”的上界意义来看并没有错,但这个界并不十分精确,也可能会使人误以为本算法无法通过1≤n 、m ≤11的测试数据,而实际上本算法可以瞬间给出m=10,n=11时的解。为了计算精确的复杂度,必须先算出DFS 得到的方案数。

考虑当前行的放置情况。如果每格只有①③两个选择,则应该有2m 种放置方案;如果每格有①②③这3个选择,且②中p 只移动一格,则应该有3m 种放置方案。然而现在的事实是:每格有①②③这3个选择,但②中p 移动2格,所以可以知道方案数应该在2m 和3m 之间。考虑第i 列,则其必然是:第i-1列采用①③达到;第i-2列采用②达到。设h i 表示前i 列的方案数,则得到h i 的递推式:

01121

2

2*i i i h h h h h ??===+

应用组合数学方法2求得其通项公式h m =m m )21(4

22)21(422??+++。注意到式子的第二项是多个绝对值小于1的数的乘积,其对整个h m 的影响甚小,故略去,得到方案数h m ≈0.85*2.414m ,符合2m

因为总共进行了n 次DFS ,每次复杂度为O(h m ),所以算法总时间复杂度为O(n*h m )=O(n*0.85*2.414m ),对m=10,n=11不超时也就不足为奇了。应用滚动数组,空间复杂度为O(2m )。

对于本题,我们已经有了公式和SCR 两种算法。公式对于答案很小的情况有效,SCR 算法在竞赛中记不住公式时对小的m 、n 有效。如果棋盘规模为 1

注意!第i 行的放置方案用到第i-1行的某格时,s2中该格应为0! 2 特征方程等方法。这里不再赘述。

n*m(m ≤10,n ≤231-1),则公式和SCR 都会严重超时。有没有一个算法能在1分钟内解决问题呢1?答案是肯定的,它仍然用到SC 思想。

此算法中应用到一个结论:给出一个图的0/1邻接矩阵G (允许有自环,两点间允许有多条路径,此时G i,j 表示i 到j 的边的条数),则从某点i 走k 步到某点j 的路径数为,k

i j G 2。本结论实际上是通过递推得到的,简单证明如下:从i 走k 步到j ,必然是从i 走k-1步到m ,然后从m 走1步到j ,根据加法原理,即G [k ]i,j =∑

G [k-1]i,m *G m,j 3。是否感到这个式子很眼熟?没错,它和矩阵乘法一模一样,即:

G [k ]=G [k-1]*G 。因为矩阵乘法满足结合律,又由G [1]=G ,所以我们得到结果:G [k ]=G k 。

下面介绍这个算法。考虑一个有2m 个顶点的图,每个顶点表示一行的覆盖状态,即SCR 算法中的s 。如果(s 1,s2)为一个放置方案,则在s2和s 1之间连一条(有向)边,则我们通过DFS 一次可以得到一个邻接矩阵G 。仍然按照逐行放置的思想来考虑,则要求我们每行选择一个覆盖状态,且相邻两行的覆盖状态(s 1,s2)应为一个放置方案,一共有n 行,则要求选择n 个状态,在图中考虑,则要求我们从初始(第0行)顶点(2m -1)走n 步到(2m -1),因为图的邻接矩阵是DFS 出来的,每条边都对应一个放置方案,所以可以保证走的每条边都合法。因此,我们要求的就是顶点(2m -1)走n 步到达(2m -1)的路径条数。由上面的结论知,本题的答案就是112,2m m k G ??。 现在的问题是,如何计算G 的n 次幂?连续O(n)次矩阵乘法吗?不可取。矩阵的规模是2m *2m ,一次普通矩阵乘法要O((2m )3)=O(8m ),O(n)次就是O(n*8m ),比SCR 算法还要差得多。其实我们可以借用二分的思想。如果要计算38的值,你会怎么算呢?直接累乘将需要进行7次乘法。一种较简单的方法是:3*3=32,32*32=34,34*34=38,只进行了3次乘法,效率高了许多4。因为矩阵乘法满足结合律,所以可以用同样的思路进行优化。这种思路用递归来实现是非常自然的,然而,本题的矩阵中可能有210*210=220=1048576个元素,如果用(未经优化的)递归来实现,将可能出现堆栈溢出。不过庆幸的是我们可以非递归实现。用bin[]保存n 的二进制的每一位,从最高位、矩阵G 开始,每次先把上一位得到的矩阵平方,如果bin[当前位]为1,则平方后再乘以G 。这种方法的时间复杂度容易算出:O(logn) 次矩阵乘法,每次O(8m ),共O(8m *logn)。

这样对于m ≤7就可以很快出解了。但对于m=n=8,上述算法都需要1s 才能出解,无法令人满意。此算法还有优化空间。

我们的矩阵规模高达2m *2m =4m ,但是其中有用的(非0的)有多少个呢?根据介绍SCR 算法时得到的h m 计算式,G 中有4m -h m =4m -0.85*2.414m 个0,对于m=8,可以算出G 中98.5%的元素都是0,这是一个非常非常稀疏的矩阵,使用三次方的矩阵乘法有点大材小用。我们改变矩阵的存储结构,即第p 行第q 列的值为value 的元素可以用一个三元组(p,q,value)来表示,采用一个线性表依行列顺序来存储这些非0元素。怎样对这样的矩阵进行乘法呢?观察矩阵乘法的计算式 1 对于如此大的数据,高精度将成为瓶颈,故这里不考虑高精度的复杂度;我没有找到能1s 出解的算法,故延长时限到1min 。

2 上标表示乘幂。

3 因为0乘任何数都为0,所以G[k-1]i,m 或G m,j 是否为0对最后的和没有影响。

4 这里假设大整数间的乘法和小整数间的乘法耗费同样的时间,实际上并非这样。但对于大数据,我们只关心结果mod 某个常数的值,所以可以进行这样的假设。

∑=b[k][j]*a[i][k]c[i][j],当a[i][k]或者b[k][j]为0时,结果为0,对结果没有影响,完全可以略去这种没有意义的运算。则得到计算稀疏矩阵乘法的算法:枚举a 中的非0元素,设为(p,q,v 1),在b 中寻找所有行号为q 的非0元素(q,r,v2),并把v 1*v2的值累加到c[p][r]1中。这个算法多次用到一个操作:找出所有行号为q 的元素,则可以给矩阵附加一个数组hp ,hp q 表示线性表中第一个行号为q 的元素的位置,若不存在则hp q =0。算出二维数组c 之后再对其进行压缩存储即可。

此矩阵乘法的时间复杂度为O(m m not b not a 420.*0.+),在最坏情况下,a.not0=b.not0=4m ,算法的复杂度为O(8m ),和经典算法相同。因为矩阵非常稀疏,

算法复杂度近似为O(4m ) 2。考虑整个算法的时间复杂度:O(logn)次矩阵乘法,每次O(4m ),则总时间复杂度O(logn*4m ),对于m ≤9也可以很快出解了,对于m=10,n=2147483647,此算法在笔者机器上(Pm 1.6G ,512M)运行时间少于20s 。虽然仍然不够理想,但已经不再超时数小时。此算法空间复杂度为O(max_not0+4m ),对于m=10,max_not0小于190000。

以上给出了公式、SCR 、矩阵乘方这3个算法,分别适用于不同的情况,本题基本解决。

读者应该已经注意到,覆盖模型和棋盘模型有很多共同点,譬如都是在矩形某些位置放入棋子(或某种形状的骨牌)来求方案数(如上例)或最优解(下面将会给出几个例题)。但不难看出,覆盖模型和棋盘模型又有着很大的不同:棋盘模型中,只要棋子所在的位置不被别的棋子攻击到即可,而覆盖模型中,棋子的攻击范围也不可以重叠。所以简单来说,覆盖模型就是攻击范围也不能重叠的棋盘模型。下面再给出一个与上例类似的覆盖模型的例题以加深印象。

【例6】3

给出n*m(1≤n 、m ≤9)的方格棋盘,用1*2的矩形的骨牌和L 形的(2*2的去掉一个角)骨牌不重叠地覆盖,求覆盖满的方案数。

【分析】

观察题目条件,只不过是比例5多了一种L 形的骨牌,因此很自然地顺着例5的思路走。本题中两种骨牌的最大长度和例5一样,所以仍然用f i,s 表示把前i-1行覆盖满、第i 行覆盖状态为s 的覆盖方案数,得到的递推式和例5完全一样:

0,21,11,2

1

m i s i s f f f ??==∑ (s 1,s2)整体作为放置方案 例5中有两种DFS 方案,其中第二种实现起来较第一种简单。但在本题中,新增的L 形骨牌让第二种DFS 难以实现,在例5中看起来有些笨拙的第一种DFS 1 此时c 是个2m *2m 的二维数组

2 其实,虽然G 为很稀疏的矩阵,但乘方后非0元素增多,不一定还是稀疏矩阵。但对于m=n=8,计算结束后,矩阵中仍然有80%的0,上述算法仍然高效。此处的复杂度是理想情况。

3 题目来源:SGU.131《Hardwood Floor 》

方案在本题却可以派上用场。回顾第一种DFS,我们有5个参数,分别为:p(当前列号),s1、s2(当前行和对应的上一行的覆盖情况),b1、b2(上一列的放置对当前列两行的影响,影响为1否则为0)。本题中可选择的方案增多,故列表给出:

容易看出,在本题中此种DFS方式实现很简单。考虑其复杂度,因为L形骨牌不太规则,笔者没能找到方案数的一维递推公式,因此无法给出复杂度的解析式。但当m=9时,算法共生成放置方案79248个,则对于n=m=9,算法的复杂度为O(9*79248),可以瞬间出解。和上例一样,本题也没有必要保存所有放置方案,也避免MLE。

那么,对于本题是否可以应用上题的矩阵算法呢?答案是肯定的,方法也类似,复杂度为O(8m*logn)。然而,对于本题却不能通过上题的稀疏矩阵算法加速,原因在于刚开始时矩阵中只有1-79248/49=70%的0,而运算结束后整个矩阵中只有2个0,根本无法达到加速效果。

由于有上题的铺垫,基本相同的本题也很快得到了解决。

【例7】1

给出n*m(n,m≤10)的方格棋盘,用1*r的长方形骨牌不重叠地覆盖这个棋盘,求覆盖满的方案数。

【分析】

本题是例5的直接扩展。如果说例5中公式比SCR好,本题可以指出当公式未知时SCR依然是可行的算法。直接思考不容易发现方法,我们先考虑r=3时的情况。首先,此问题有解当且仅当m或n能被3整除。更一般的结论是:用1*r的骨牌覆盖满m*n的棋盘,则问题有解当且仅当m或n能被r整除。当r=2时,则对应于例5中m、n至少有一个是偶数的条件。此结论的组合学证明从略。1题目来源:经典问题改编

不同于例5,1*3骨牌的“攻击范围”已经达到了3行,可以想像例5中的表示方法已经无法正确表示所有状态,但其思路依然可以沿用。例5中用f i,s 表示把前i-1行覆盖满、第i 行覆盖状态为s 的覆盖方案数,是因为当前行的放置方案至多能影响到上一行,状态中只要包含一行的覆盖状态即可消除后效性。本题中当前行的放置方案可以影响到上两行,故可以想到应保存两行的覆盖状态以消除后效性,即增加一维,用f i,s 1,s2表示把前i-2行覆盖满、第i-1行覆盖状态为s 1、第i 行覆盖状态为s2的覆盖方案数。先不论上述表示方法是否可行(答案是肯定的),r=2时状态有2维,r=3时有3维,推广后状态变量居然有r 维,这样的方法不具有推广价值,而且空间复杂度也太高。

仔细分析上述方案,可以发现其失败之处。s 1的第p 位s 1p 为1(覆盖)时,s2p 是不可能为0的(要求覆盖满),则这两位(s 1p , s2p )的(0,0),(0,1),(1,0),(1,1)四种组合中有一种不合法,而上述状态表示方法却冗余地保存了这个组合,造成空间复杂度过高,也进行了多余的计算1。通过上面的讨论可以知道,每一位只有3种状态,引导我们使用三进制。我们用f i,s 表示把前i-2行覆盖满、第i-1和第i 行覆盖状态为s 的覆盖方案数,但这里状态s 不再是二进制,而是三进制:s p =0表示s 1p =s2p =0;s p =1表示s 1p =0,s2p =1;s p =2表示s 1p =s2p =1。这样,我们就只保留了必要的状态,空间和时间上都有了改进。当r=4时,可以类推,用四进制表示三行的状态,r=5时用五进制……分别写出r=2,3,4,5的程序,进行归纳,统一DFS 的形式,可以把DFS(p,s 1,s2)分为两部分:①for i=0 to r -1 do DFS(p +1,s 1*r+i,s2*r+(i +1)mod r);②DFS(p+r,s 1*r r +r r -1,s2*r r +r r -1) 问题解决。但DFS 的这种分部方法是我们归纳猜想得到的,并没有什么道理,其正确性无法保证,我们能否通过某种途径证明它的正确性呢?仍以r=3为例。根据上面的讨论,s p 取值0到2,表示两行第p 位的状态,但s p 并没有明确的定义。我们定义s p 为这两行的第p 位从上面一行开始向下连续的1的个数,这样的定义可以很容易地递推,递推式同上两例没有任何改变,却使得上述DFS 方法变得很自然2。 分析算法的时间复杂度,同例5一样需要用到DFS 出的方案个数h m ,并且仿照例5中h m 的递推式,我们可以得到3:

1*i

i j j j r h r h r h h ??==+ (i=0 ~ r-1, j=r ~ m)

理论上我们可以根据递推式得到例5中那样的精确的通项公式,但需要解高于三次的方程,且根多数为复数,无法得到例5那样简单优美的表达式,这里仅给出r=2..5,m=10时h m 的值依次为:5741,77772,1077334,2609585,9784376。对于推广后的问题,例5的矩阵算法依然可行,但此时空间将是一个瓶颈。

【例8】4

给出n*m(n ≤150,m≤10)的棋盘,要在上面放置2*3的骨牌,有一些方格无法放置,求最多能放置多少个。

【分析】

根据上例的讨论,此题可以使用三进制来表示状态。s p 取值0到2,表示两 1

同样的,例4中的状态表示方法也有冗余,但因为合法方案很少,又因为位运算的使用,算法仍然高效。 2 请尝试写出r=3时用这种定义的递推式和相应的程序

3 其实从DFS 过程可以很直观地看出

4 题目来源:CEOI.2002《Bugs Integrated, Inc.》;PKU.1038

行第p 位的状态,定义s p 为这两行的第p 位从上面一行开始向下连续的1的个数。我们依旧用s 1表示当前行状态,s2表示上一行状态,p 表示位置,num 表示从状态s 1转移到s2能增加的骨牌个数。

设f i,j 表示第i 行状态为j 的最大放置数。我们可以得到方程:

,11,2max{}i s i s f f num ?=+ 同样的方程之前已经见过多次,不再多作解释,重点放在如何得到s 1和s2。

对于当前行第p 位,如果放置骨牌,则有两种放置方式:①竖放,s 1当前位置p 及后一个位置p +1都为2,s2要求p 和p +1列留空2格,即p,p +1列都为0,这样能刚好放置一块骨牌且不留空隙。对应的DFS 调用方式:DFS(p+2,s 1*9+8,s2*9,num +1); ②横放,s 1的三个位置都要填满,即p,p +1,p+2列均为2,s2要求p,p +1,p+2列均为1,调用DFS(p+3,s 1*27+26,s2*27+13,num +1);

如果第p 位不放置骨牌,我们还要把s2的状态转移到s 1中以备下次继续转移。形象地说就是虚填一些方块使得下次放置骨牌可以不留空隙。对于某一位置,我们可以分3种情况DFS 以达到目的。

(1) s 1的当前行空闲,上一行放满。这个状态可以由s2的放满状态转移得到。

DFS(p+1,s 1*3+1,s2*3+2,num);

(2) s 1的当前行和上一行都空闲。这个状态可以由s2的当前行空闲,上一行

放满的状态转移得到。

DFS(p+1,s 1*3,s2*3+1,num);

(3) s 1的当前行放满。可以由s2的放满状态转移得到。

DFS(x+1,s 1*3+2,s2*3+2,num); (放置了1*1的虚骨牌在s 1的当前位置)

为加深理解请看图,图中浅蓝色为s 1中填骨牌的区域,黄色为s2中之前已经填满的区域:上图为横放一个骨牌的情形,不多解释;下图中为竖着放置骨牌的情形,先放一1*1的虚骨牌(红色),再放一个骨牌。

通过上面的讨论,此题基本解决,剩下的是关于不能放置的点(黑点)的处理

技巧。一种方法是:开两个bool 数组map 1,map2来标志某个位置是否能放置骨牌。map 1i,j =1表示(i,j)位置横着放置骨牌不会覆盖到黑点。map2i,j =1表示(i,j)位

置竖着放置骨牌不会覆盖到黑点。先把两个数组初始化为1,读入黑点坐标时,在map1把以黑点坐标为右上角的2*3区域置为0,在map2同样把黑点坐标为左上角的3*2区域置为0。当然,不能忘记把map22,x全置为0。这样,我们只要在意图填充骨牌之前判断一下map是否为1即可。截至07年5月,笔者的代码在PKU1038是唯一少于1k的,速度也比代码长度第二名(1.2k)的快一倍。问题解决。事实上,如果上例中的棋盘上有不能放置的格子,本例的放置虚骨牌的方法同样可行,具体实现留给读者思考。

上文通过对几个经典的应用状态压缩的题目的详解,从应用的角度描述了状态压缩的一般思路。那么如何理解其本质呢?

纵观上文讨论的题目,几乎都是普普通通的一个递推公式或者一个状态转移方程,只不过其中的一维或多维是“压缩的”,即把一个状态(或一个方案、一个集合)压缩成一个整数。这很明显是一个Hash的过程,所以SCDP又被称为Hash DP或集合DP。为求统一,本文后面依然称其为状态压缩。除去这一点区别,我们发现它和普通递推/DP没有什么差别,都是从已有的状态推知新的状态,所做的决策都没有后效……那我们为什么要用状态压缩,即状态压缩的动机是什么?

回想前述的题目,以例8为例,如果我们不状态压缩,能否正确解答呢?先来进行一下尝试。尝试f i表示前i行最多能放的骨牌数,我们发现这样的表示方法太“粗糙”,根本无法表达出行与行之间因放置骨牌产生的细微的变化,这样的状态表示无法得到正确的递推或状态转移关系。因此我们增加将每行“细化”为每格的一维,即状态压缩。所以,状态太粗糙以致无法正确转移是状态压缩的一个动机。

上面的例题“主战场”都是棋盘,未免有些单调,让人产生状态压缩应用不广泛的想法。下面几个跟图论有关的题目应该会使读者打消这个念头。

【例9】1

有n(n≤20)头牛和m(m≤20)个牧场,每头牛都有若干个喜欢的牧场,它们都不喜欢和别的牛在同一个牧场,问有多少种分配方案使得每头牛都有自己的牧场。

【分析】

这是个很直观的图论问题,更确切的,题目给出的是一个二分图,而我们要求这个二分图完备匹配2的个数。这是一个NPH问题,确切地说是#PC(#P-Complete)问题。之前P、NP、NPC等概念仅对判定性问题(Decision Problem)成立,另有函数问题(Function Problem),#P就是其中的一类,专门研究计数问题。而#PC则是#P中最难的一类,任何#P问题都能用对数级空间归约到#PC。每个#P问题都有其对应的判定性版本,显然对解的计数要比仅仅判定是否存在解要困难,因此如果一个#P问题对应的判定性版本是NPC的,则这个#P问题一定是NPH的。

既然此例是#PC的,则它同NPC/NPH一样尚未有多项式时间的算法,我们1题目来源:经典问题;PKU.2441《Arrange the Bulls》

2此处的“完备匹配”是指能完全覆盖一边的点的匹配,不要求两边都被完全覆盖。

只能退而求其次,来找近似算法或较高速的指数级算法。对于本例,SCR 就是一种简单可行的指数级确定性算法。

引例中,我们一行一行地放置棋子,因为那是一种很自然的阶段。类比之,本题中以牛的编号作为阶段,用一个m 位的二进制数表示牧场的占用情况,1表示已经占用。

考虑m=5,状态s=01101表示牧场1、3、4已经被牛占用。因为我们以牛的编号作为阶段,所以当前已经考虑到第3头牛。当前阶段和之前的阶段有什么关系呢?其实再次类比引例,我们只要考虑第3头牛匹配哪个牧场即可,即: 01101011003,1010013,3001013,4***f f g f g f g =++

其中g 是0/1邻接矩阵。

我们做到了不重不漏,因此是正确的。推广到一般情况,得到:

0(),121

*i s z s i s f f f g +?==∑

其中z(s)表示二进制数s 中1的个数,可以用递推式z(s)=z(s/2)+s%2很容易得到。方程的意义很显然,不再多作叙述,最后的答案就是∑f s (z(s)=n)。分析时间复杂度,共2m 个状态,转移耗费O(n),故时间复杂度O(n*2m ),这样,一道和棋盘没有关系的问题得到了解决1。

如果感觉上述算法难以理解2,

我们可以从另一个角度来重新阐述一下上述算法。约定S 表示状态,但不再是一个二进制数,而是一个集合,被占用的牧场的集合,例如S={1,3,4}表示牧场1、3、4已经被牛占用;|S|表示集合S 中元素的个数,例如|{1,3,4}|=3;f S 表示前|S |头牛分别占据集合S 中的牧场的合法方案数。同前所述,为了减小问题的规模(递归思想),我们枚举第|S|头牛占据的牧场,从而把问题的规模减小为|S|-1。再以S={1,3,4}为例,此时第1,3,4号牧场被占据,我们枚举第|S|=3头牛的匹配方案,即(3,1)、(3,3)、(3,4)这3种,不重不漏,得:

3,43,33,1{1,3,4}{1,3}{1,4}{3,4}***=++f f g f g f g

此式比用二进制的表示方法意义更加明显,一般地有:

{}||,1

*S S x S x

x S f f f g ?∈==∑

最后结果是∑f S (|S|=n)。对于本题,从集合的角度理解起来比二进制更加直观,对于后面的几个图论方面的题目亦是如此3。当然,这种思路实现起来依然是要用二进制的。

【例10】4

给出一个有向图(n ≤20),求这个图的合法的拓扑序列的个数。 1

本题还可以进行推广,求一个二分图边数为k 的匹配的个数,留给读者思考。 2 如果读者从头一直认真地看到这里,真的会难以理解么……?其实是为下文作铺垫…

3 请读者尝试从集合的角度理解前面的例题

4 题目来源:经典问题

【简析】

相比上题,本题更容易确定状态:状态S表示当前已经访问过的节点的集合,f S表示访问过S中的点的合法的拓扑序的个数。有了合适的状态表示,再联系递推的一般思路,我们发现只需要枚举最后到达的点即可,具体细节留给读者思考。

【例11】1

给出n(n≤15)个字符串,求一个最短的串S,使得给出的那些串都是S的子串。如果有多个解输出字典序最小的一个。

【分析】

初看题目,很难想像这和图论有什么关系。我们一步一步来分析。

首先可以想到的是,如果某个串s1是另外一个串s2的子串,则可以将s1除去不考虑,这样可以减小问题的规模2。

1题目来源:经典问题(Shortest Common Superstring);PKU.1795《DNA Laboratory》

2事实上,此算法的正确性依赖于这个看似显然的步骤。

动态规划之状态压缩

状态压缩 Abstract 信息学发展势头迅猛,信息学奥赛的题目来源遍及各行各业,经常有一些在实际应用中很有价值的问题被引入信息学并得到有效解决。然而有一些问题却被认为很可能不存在有效的(多项式级的)算法,本文以对几个例题的剖析,简述状态压缩思想及其应用。 Keywords 状态压缩、Hash、动态规划、递推 Content Introducti o n 作为OIers,我们不同程度地知道各式各样的算法。这些算法有的以O(logn)的复杂度运行,如二分查找、欧几里德GCD算法(连续两次迭代后的余数至多为 原数的一半)、平衡树,有的以)运行,例如二级索引、块状链表,再往上有O(n)、O(n p log q n)……大部分问题的算法都有一个多项式级别的时间复杂度上界1,我们一般称这类问题2为P (deterministic Polynomial-time)类问题,例如在有向图中求最短路径。然而存在几类问题,至今仍未被很好地解决,人们怀疑他们根本没有多项式时间复杂度的算法,NPC(NP-Complete)和NPH(NP-Hard)就是其中的两类,例如问一个图是否存在哈密顿圈(NPC)、问一个图是否不存在哈密顿圈(NPH)、求一个完全图中最短的哈密顿圈(即经典的Traveling Salesman Problem货郎担问题,NPH)、在有向图中求最长(简单)路径(NPH),对这些问题尚不知有多项式时间的算法存在。P和NPC都是NP(Non-deterministic Polynomial-time)的子集,NPC则代表了NP类中最难的一类问题,所有的NP类问题都可以在多项式时间内归约到NPC问题中去。NPH包含了NPC和其他一些不属于NP(也更难)的问题,NPC问题的函数版本(相对于判定性版本)一般是NPH的,例如问一个图是否存在哈密顿圈是NPC的,但求最短的哈密顿圈则是NPH的,原因在于我们可以在多项式时间内验证一个回路是否真的是哈密顿回路,却无法在多项式时间内验证其是否是最短的,NP类要求能在多项式时间内验证问题的一个解是否真的是一个解,所以最优化TSP问题不是NP的,而是NPH的。存在判定性TSP问题,它要求判定给定的完全图是否存在权和小于某常数v的哈密顿圈,这个问题的解显然可以在多项式时间内验证,因此它是NP 1请注意,大O符号表示上界,即O(n)的算法可以被认为是O(n2)的,O(n p log q n)可以被认为是O(n p+1)的。2在更正式的定义中,下面提到的概念都只对判定性问题或问题的判定版本才存在(NPH除外)。Levin给出了一个适用于非判定问题的更一般的概念,但他的论文比Cook的晚发表2年。

动态规划讲解大全(含例题及答案)

动态规划讲解大全 动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。1957年出版了他的名著Dynamic Programming,这是该领域的第一本著作。 动态规划问世以来,在经济管理、生产调度、工程技术和最优控制等方面得到了广泛的应用。例如最短路线、库存管理、资源分配、设备更新、排序、装载等问题,用动态规划方法比用其它方法求解更为方便。 虽然动态规划主要用于求解以时间划分阶段的动态过程的优化问题,但是一些与时间无关的静态规划(如线性规划、非线性规划),只要人为地引进时间因素,把它视为多阶段决策过程,也可以用动态规划方法方便地求解。 动态规划程序设计是对解最优化问题的一种途径、一种方法,而不是一种特殊算法。不象前面所述的那些搜索或数值计算那样,具有一个标准的数学表达式和明确清晰的解题方法。动态规划程序设计往往是针对一种最优化问题,由于各种问题的性质不同,确定最优解的条件也互不相同,因而动态规划的设计方法对不同的问题,有各具特色的解题方法,而不存在一种万能的动态规划算法,可以解决各类最优化问题。因此读者在学习时,除了要对基本概念和方法正确理解外,必须具体问题具体分析处理,以丰富的想象力去建立模型,用创造性的技巧去求解。我们也可以通过对若干有代表性的问题的动态规划算法进行分析、讨论,逐渐学会并掌握这一设计方法。 基本模型 多阶段决策过程的最优化问题。 在现实生活中,有一类活动的过程,由于它的特殊性,可将过程分成若干个互相联系的阶段,在它的每一阶段都需要作出决策,从而使整个过程达到最好的活动效果。当然,各个阶段决策的选取不是任意确定的,它依赖于当前面临的状态,又影响以后的发展,当各个阶段决策确定后,就组成一个决策序列,因而也就确定了整个过程的一条活动路线,如图所示:(看词条图) 这种把一个问题看作是一个前后关联具有链状结构的多阶段过程就称为多阶段决策过程,这种问题就称为多阶段决策问题。 记忆化搜索 给你一个数字三角形, 形式如下: 1 2 3 4 5 6 7 8 9 10 找出从第一层到最后一层的一条路,使得所经过的权值之和最小或者最大. 无论对与新手还是老手,这都是再熟悉不过的题了,很容易地,我们写出状态转移方程:f(i, j)=a[i, j] + min{f(i+1, j),f(i+1, j + 1)} 对于动态规划算法解决这个问题,我们根据状态转移方程和状态转移方向,比较容易地写出动态规划的循环表示方法。但是,当状态和转移非常复杂的时候,也许写出循环式的动态规划就不是那么

动态规划状态转移方程

1.资源问题1 -----机器分配问题 F[I,j]:=max(f[i-1,k]+w[i,j-k]) 2.资源问题2 ------01背包问题 F[I,j]:=max(f[i-1,j-v[i]]+w[i],f[i-1,j]); 3.线性动态规划1 -----朴素最长非降子序列 F[i]:=max{f[j]+1} 4.剖分问题1 -----石子合并 F[i,j]:=min(f[i,k]+f[k+1,j]+sum[i,j]); 5.剖分问题2 -----多边形剖分 F[I,j]:=min(f[i,k]+f[k,j]+a[k]*a[j]*a[i]); 6.剖分问题3 ------乘积最大 f[i,j]:=max(f[k,j-1]*mult[k,i]); 7.资源问题3 -----系统可靠性(完全背包) F[i,j]:=max{f[i-1,j-c[i]*k]*P[I,x]} 8.贪心的动态规划1 -----快餐问题 F[i,j,k]:=max{f[i-1,j',k']+(T[i]-(j-j')*p1-(k-k')*p2) div p3} 9.贪心的动态规划2 -----过河 f[i]=min{{f(i-k)} (not stone[i]) {f(i-k)}+1} (stone[i]); +贪心压缩状态 10.剖分问题4 -----多边形-讨论的动态规划 F[i,j]:=max{正正 f[I,k]*f[k+1,j]; 负负 g[I,k]*f[k+1,j]; 正负 g[I,k]*f[k+1,j]; 负正 f[I,k]*g[k+1,j];} g为min 11.树型动态规划1 -----加分二叉树 (从两侧到根结点模型)

项目计划动态管理系统

《项目计划动态管理系统》 系统分析 详细设计 深圳市伟博多媒体电脑有限公司

目录 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ §1 系统工作流程 §2 业务流程图 §3 业务分析 §4 功能设计,报表设计 §5 系统安全性及用户权限的设置 §6 系统编码 §7 系统总体数据流程图 §8 数据库设计 §9 功能模块描述

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

§1 系统工作流程 一、申请立项 1.申请工程立项和申请项目计划可以同时进行,工程立项只申请一次,项目计划申请今后每季度申请一次,但不再申请年度计划。项目计划的第一次申请作为项目立项申请。 2.申请工程立项由局项目主管部门(基建项目为规划设计院)提出的,一个工程可能包含若干项目;申请项目立项是由负责项目实施的各基层部门(分部级)进行。立项后的工程或项目都拥有一个工程编号,一个工程仅有唯一的编号,若干项目可能共用一个工程编号。 3.工程立项时,给出总投资概算、分投资概算。项目申请时,如果几个部门的项目同属于一个工程时,需要控制计划资金。实施同一工程的各基层部门的项目计划总投资之和不能超过工程概算总投资,各部门各类分项投资的和不能超过概算分投资(概算资金不严格限制)。部门项目季度计划总投资和分投资的累计值不能超过该项目计划总投资和分投资(项目投资严格限制)。 4.工程立项申请时,录入项目名称、主管部门、必要性、实施内容、建设规模、总投资、建设工期、分投资(建筑工程、安装工程、设备材料、其他)、申请日期、设备材料(名称、规格、数量、单价、总价、计算依据);项目立项申请时录入项目名称、实施部门、必要性、实施内容、建设规模、总投资、建设工期、分投资(建筑工程、安装工程、设备材料、其他)、项目负责人信息、实施人员、申请日期,设备材料(名称、规格、

NOIP 2017全国青少年信息学奥林匹克联赛提高组初赛试题答案

NOIP 2017全国青少年信息学奥林匹克联赛提高组初赛试题答案 ? 一、单项选择题(共 15 题,每题 1.5 分,共计 22.5 分;每题有且仅有一个正确选项)? 1. 从( )年开始,NOIP 竞赛将不再支持 Pascal 语言。 A. 2020 B. 2021 C. 2022 D. 2023 ? 2.在 8 位二进制补码中,10101011 表示的数是十进制下的( )。 A. 43 B. -85 C. -43 D.-84 ? 3.分辨率为 1600x900、16 位色的位图,存储图像信息所需的空间为( )。 A. 2812.5KB B. 4218.75KB C. 4320KB D. 2880KB ? 4. 2017年10月1日是星期日,1949年10月1日是( )。 A. 星期三 B. 星期日 C. 星期六 D. 星期二 ? 5. 设 G 是有 n 个结点、m 条边(n ≤m)的连通图,必须删去 G 的( )条边,才能使得 G 变成一棵树。 A.m–n+1 B. m-n C. m+n+1 D.n–m+1 ? 6. 若某算法的计算时间表示为递推关系式: T(N)=2T(N/2)+NlogN T(1)=1 则该算法的时间复杂度为( )。 A.O(N) B.O(NlogN) C.O(N log2N) D.O(N2) ? 7. 表达式a * (b + c) * d的后缀形式是()。 A. abcd*+* B. abc+*d* C. a*bc+*d D. b+c*a*d

? 8. 由四个不同的点构成的简单无向连通图的个数是( )。 A. 32 B. 35 C. 38 D. 41 ? 9. 将7个名额分给4个不同的班级,允许有的班级没有名额,有( )种不同的分配方案。 A. 60 B. 84 C. 96 D.120 ? 10. 若f[0]=0, f[1]=1, f[n+1]=(f[n]+f[n-1])/2,则随着i的增大,f[i]将接近与( )。 A. 1/2 B. 2/3 D. 1 ? 11. 设A和B是两个长为n的有序数组,现在需要将A和B合并成一个排好序的数组,请问任何以元素比较作为基本运算的归并算法最坏情况下至少要做( )次比较。 A. n2 B. nlogn C. 2n D.2n-1 ? 12. 在n(n>=3)枚硬币中有一枚质量不合格的硬币(质量过轻或质量过重),如果只有一架天平可以用来称重且称重的硬币数没有限制,下面是找出这枚不合格的硬币的算法。请把 a-c三行代码补全到算法中。 a. A XUY b. A Z c. n |A| 算法Coin(A,n) 1. k n/3 2. 将A中硬币分成X,Y,Z三个集合,使得|X|=|Y|=k, |Z|=n-2k 3. if W(X)≠W(Y) //W(X), W(Y)分别为X或Y的重量 4. then_______ 5. else_______ 6. __________ 7. if n>2 then goto 1 8. if n=2 then 任取A中1枚硬币与拿走硬币比较,若不等,则它不合格;若相等,则A 中剩下的硬币不合格 9. if n=1 then A中硬币不合格 正确的填空顺序是( )。 A. b,c,a B. c,b,a C. c,a,b D.a,b,c ?

3 (修改)大规模状态空间中的动态规划和强化学习问题

3 大规模状态空间中的动态规划和强化学习问题 本章我们将讨论大规模状态空间中的动态规划和强化学习问题。对于这类问题,我们一般很难求得问题的精确解,只能得到问题的近似解。前面章节所介绍的一些算法,如值迭代、策略迭代和策略搜索,无法直接用于这类问题。因此,本章将函数近似引入这些算法,提出三类基于函数近似的算法版本,分别是近似值迭代、近似策略迭代和近似策略搜索。本章将从理论和实例两个角度分析算法的收敛性,讨论如何获取值函数逼近器的方法,最后比较分析三类算法的性能。 3.1 介绍 第二章详细介绍了DP/RL中三类经典算法,这三类算法都需要有精确的值函数及策略表示。一般来说,只有存储每一个状态动作对回报值的估计值才能得到精确地Q值函数,同样V值函数只有存储每一个状态的回报值的估计值才能得到;精确的策略描述也需要存储每一个状态对应的动作。如果值函数中某些变量,比如某些状态动作对、状态等,存在很多个或者无穷多个潜在值(又或者这些值是连续的),那么我们就无法精确描述对应的Q值函数或者V值函数,因此,考虑将值函数和策略通过函数近似的方式来表示。由于实际应用中大部分问题都存在大规模或者连续状态空间,因此,函数近似方法是求解动态规划和强化学习问题的基础。 逼近器主要可以分为两大类:带参的和非参的。带参的逼近器主要是从参数空间到目标函数空间的映射。映射函数及参数的个数由先验知识给定,参数的值由样本数据进行调整。典型的例子是对一组给定的基函数进行加权线性组合,其中权重就是参数。相比之下,非参的逼近器通过样本数据直接得到。本质上,非参的函数逼近器也是含带参数的,只是不像带参的函数逼近器,参数的个数及参数的值直接有样本数据决定。例如,本书中所讨论的基于核函数的逼近器就是带参数的函数逼近器,它为每一个数据点定义一个核函数,并对这些核函数做加权线性组合,其中权重就是参数。 本章主要对大规模状态空间中动态规划和强化学习问题进行广泛而深入的讨论。第二章中所介绍的三类主要算法,值迭代、策略迭代和策略搜索,将与函数近似方法相结合,获得三类新的算法,分别是近似值迭代、近似策略迭代以及近似策略搜索。本章将从理论和实例两个角度讨论算法的收敛性,并对比分析三类算法的性能。关于值函数近似与策略逼近的一些其他重要问题,本章也将给予讨论。为了帮助读者更好的阅读本章的内容,图3.1给出一个本章的内容脉络图。

状态压缩dp

状态压缩dp 炮兵阵地 Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 8971 Accepted: 3169 Description 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示: 如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。 现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。 Input 第一行包含两个由空格分割开的正整数,分别表示N和M; 接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。 Output 仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。 Sample Input 5 4

PHPP PPHH PPPP PHPP PHHP Sample Output 6 Source Noi 01 这道题是一道典型的状态压缩DP。 注意到每一行炮兵的摆放只和前两行有关系,便可以记录一个f数组,f[h,i,j]表示第h行的状态为st[i],第h-1行的状态为st[j]。 现在的关键就是如何快速求f[h,i,j]。 注意到求f数组跟状态数有关,那么便优化状态。 首先是状态的存储。注意到m范围较小,便可以按行存储。对于每一行,如果一个格子放了炮兵,就看成1,否则看成0,然后记下对应数列的十进制值即可。但这样共有2^m种状态,显然太多了。注意到有很多状态其实是不可能的(即某个炮兵附近还有炮兵),我们就可以再预处理状态时加一些判断。那么如何高效判断呢?对了,可以用位运算! 判断过程如下: for i:=0 to 1 shl m-1 do begin if i and (i shl 1)<>0 then continue;//错一位进行判断 if i and (i shl 2)<>0 then continue;//错两位进行判断 inc(sl); st[sl]:=i; count[sl]:=calc(i);//count数组记录每个序列中1的个数 end; 解决了存储问题后,整个程序就很简单了,剩下的DP不再赘述,详见程序。贴代码:https://www.360docs.net/doc/1d8259992.html,/code/view/18264/ var f:array[1..100,1..70,1..70] of integer; map:array[1..100] of integer; st,count:array[1..70] of integer; n,m,sl:integer; function calc(x:integer):integer; var sum:integer;

动态规划经典教程

动态规划经典教程 引言:本人在做过一些题目后对DP有些感想,就写了这个总结: 第一节动态规划基本概念 一,动态规划三要素:阶段,状态,决策。 他们的概念到处都是,我就不多说了,我只说说我对他们的理解: 如果把动态规划的求解过程看成一个工厂的生产线,阶段就是生产某个商品的不同的环节,状态就是工件当前的形态,决策就是对工件的操作。显然不同阶段是对产品的一个前面各个状态的小结,有一个个的小结构成了最终的整个生产线。每个状态间又有关联(下一个状态是由上一个状态做了某个决策后产生的)。 下面举个例子: 要生产一批雪糕,在这个过程中要分好多环节:购买牛奶,对牛奶提纯处理,放入工厂加工,加工后的商品要包装,包装后就去销售……,这样没个环节就可以看做是一个阶段;产品在不同的时候有不同的状态,刚开始时只是白白的牛奶,进入生产后做成了各种造型,从冷冻库拿出来后就变成雪糕(由液态变成固态=_=||)。每个形态就是一个状态,那从液态变成固态经过了冰冻这一操作,这个操作就是一个决策。 一个状态经过一个决策变成了另外一个状态,这个过程就是状态转移,用来描述状态转移的方程就是状态转移方程。 经过这个例子相信大家对动态规划有所了解了吧。 下面在说说我对动态规划的另外一个理解: 用图论知识理解动态规划:把动态规划中的状态抽象成一个点,在有直接关联的状态间连一条有向边,状态转移的代价就是边上的权。这样就形成了一个有向无环图AOE网(为什么无环呢?往下看)。对这个图进行拓扑排序,删除一个边后同时出现入度为0的状态在同一阶段。这样对图求最优路径就是动态规划问题的求解。 二,动态规划的适用范围 动态规划用于解决多阶段决策最优化问题,但是不是所有的最优化问题都可以用动态规划解答呢? 一般在题目中出现求最优解的问题就要考虑动态规划了,但是否可以用还要满足两个条件: 最优子结构(最优化原理) 无后效性 最优化原理在下面的最短路径问题中有详细的解答; 什么是无后效性呢? 就是说在状态i求解时用到状态j而状态j就解有用到状态k…..状态N。 而求状态N时有用到了状态i这样求解状态的过程形成了环就没法用动态规划解答了,这也是上面用图论理解动态规划中形成的图无环的原因。 也就是说当前状态是前面状态的完美总结,现在与过去无关。。。 当然,有是换一个划分状态或阶段的方法就满足无后效性了,这样的问题仍然可以用动态规划解。 三,动态规划解决问题的一般思路。 拿到多阶段决策最优化问题后,第一步要判断这个问题是否可以用动态规划解决,如果不能就要考虑搜索或贪心了。当却定问题可以用动态规划后,就要用下面介绍的方法解决问题了:(1)模型匹配法: 最先考虑的就是这个方法了。挖掘问题的本质,如果发现问题是自己熟悉的某个基本的模型,就直接套用,但要小心其中的一些小的变动,现在考题办都是基本模型的变形套用时要小心条件,三思而后行。这些基本模型在先面的分类中将一一介绍。 (2)三要素法 仔细分析问题尝试着确定动态规划的三要素,不同问题的却定方向不同: 先确定阶段的问题:数塔问题,和走路问题(详见解题报告) 先确定状态的问题:大多数都是先确定状态的。 先确定决策的问题:背包问题。(详见解题报告) 一般都是先从比较明显的地方入手,至于怎么知道哪个明显就是经验问题了,多做题就会发现。 (3)寻找规律法: 这个方法很简单,耐心推几组数据后,看他们的规律,总结规律间的共性,有点贪心的意思。 (4)边界条件法 找到问题的边界条件,然后考虑边界条件与它的领接状态之间的关系。这个方法也很起效。 (5)放宽约束和增加约束 这个思想是在陈启锋的论文里看到的,具体内容就是给问题增加一些条件或删除一些条件使问题变的清晰。 第二节动态规划分类讨论

noip算法总结2016

算法总结 一、动态规划和递推 dp一般的解题步骤: 分析问题,弄清题意——从原问题中抽象出模型——根据模型设计状态,要求状态满足最优子结构和无后效性——直接设计状态有难度的话则需要考虑转化模型——根据设计的状态考虑转移——如果过不了题目要求的数据范围,则需要考虑优化 由于动态规划涉及的内容太多,只言片语难以讲清,所以附件中放了很多篇关于动态规划的文章,大部分系原创,并附上了一些经典的论文,主要讲了DP的优化,一些特殊的状态设计技巧 Dp和递推没有本质区别,都是用一些状态来描述问题,并记录下一些信息,根据已知信息推出未知信息,直到得到问题的解 关于DP的优化有两篇神级论文,放在附件里面了,写的非常好。 二、图论及网络流 最小生成树:克鲁斯卡尔算法和普利姆算法, ——重要性质1:最小生成树上任意两点的路径的最大边最小 ——重要性质2:最小生成树的多解(方案个数)只与相同权值的的边有关(省队集训题生成树计数) 最短路:spfa算法、堆+迪杰斯特拉算法 Spfa算法是基于松弛技术的,随机图效果极佳,最坏(网格图或存在负权环)O(nm),适用于任意图,能够判断负权环 ——判负权环的方法:记录每个点当前从原点到它的最短路上边的条数,如果某次更新后这个条数>n-1则存在负权环 堆+迪杰斯特拉则是用了贪心的思想,不断扩大确定dist的集合,同时更新dist,如果边权有负值就不能做,复杂度是O((n+m)logn)的 拓扑排序:可以将有向图转化为一个线性的序列,满足一个点所有的前驱结点都出现在这个点在序列中的位置之前。可以判断这个有向图是否有环 ——一个简单而实用的扩展:给树做类top排序,可以有类似的功能,即每次去掉叶子结点,将树转化为一个具有拓扑关系的序列 ——再扩展:树同构判断,可用类top确定树根是谁,再最小表示法+hash即可 强连通分量、缩点:tarjan算法 核心是每个点记一个时间戳ti[i], 另外low[i]表示i点能延伸出的搜索树中节点的ti[i]的最小值,还要维护个栈记当前路径上的点,low[i]初始化为ti[i],如果搜完i了,ti[i]=low[i]则当前栈顶到i的所有点会在一个强连同分量内。

动态规划的基本概念

动态规划的基本概念 基本概念 设我们研究某一个过程,这个过程可以分解为若干个互相联系的阶段。每一阶段都有其初始状态和结束状态,其结束状态即为下一阶段的初始.状态。第一阶段的初始状态就是整个过程的初始状态,最后一阶段的结束状态就是整个过程的结束状态。在过程的每一个阶段都需要作出决策,而每一阶段的结束状态依赖于其初始状态和该阶段的决策。动态规划问题就是要找出某种决策方法, 使过程达到某种最优效果。 这种把问题看作前后关联的多阶段过程称为多阶段决策过程, 可用图9.1表示。下面介绍动态规划的术语和基本概念。 (l)阶段 把所研究的过程恰当地分为若干个互相联系的相对独立过程。 (2)状态变量 用来描述系统所处状态的变量称为状态变量。通常用s k 表示第k 阶段的初始状态,则s k +1表示第k 阶段结束时(也就是第k+l 阶段开始时)过程的状态。 通常要求状态变量具有无后效性, 即过程在第k 阶段以后的变化只与该阶段结束时的状态有关, 而与系统如何到达此状态的过程无关。 (3)决策变量的状态转移方程。系统在第k 阶段中的变化过程, 通常我们并不关心,但我们希望知道该阶段的初始状态与结束状态之间的关系。我们用以影响该系统的手段,也用一个变量x k 表示,称为决策变量, 则第k 阶段结束时的状态s k +1是决策变量x k 和初始状态s k 的函数, 即 s k +1=T (s k ,x k ) (9-1) (9-1)称为状态转移方程。 (4)权函数 反映第k 阶段决策变量x k 的效益函数W k (s k ,x k ) 称为权函数。 (5)指标函数 判断整个过程优劣的数量指标称为指标函数。当第k 阶段初始状态为s k 时,设我们在此阶段及以后各阶段均采取最优策略时,所获得的效益为f k (s k ), 那么有 ))}(),,(({)(11++∈=k k k k k k D x k k s f x s W F opt s f k k (9-2) 其中opt 表示最优,按具体问题可取为max 或min , D k 是决策变量x k 的定义域;F k 是某一个函数; s k +1=T (s k ,x k ). 图9.1

动态规划习题精讲

信息学竞赛中的动态规划专题 哈尔滨工业大学周谷越 【关键字】 动态规划动机状态典型题目辅助方法优化方法 【摘要】 本文针对信息学竞赛(面向中学生的Noi以及面向大学生的ACM/ICPC)中的动态规划算法,从动机入手,讨论了动态规划的基本思想和常见应用方法。通过一些常见的经典题目来归纳动态规划的一般作法并从理论上加以分析和说明。并介绍了一些解决动态规划问题时的一些辅助技巧和优化方法。纵观全文可知,动态规划的关键在于把握本质思想的基础上灵活运用。 【目录】 1.动态规划的动机和基本思想 1.1.解决重复子问题 1.2.解决复杂贪心问题 2.动态规划状态的划分方法 2.1.一维状态划分 2.2.二维状态划分 2.3.树型状态划分 3.动态规划的辅助与优化方法 3.1.常见辅助方法 3.2.常见优化方法 4.近年来Noi动态规划题目分析 4.1 Noi2005瑰丽华尔兹 4.2 Noi2005聪聪与可可 4.3 Noi2006网络收费 4.4 Noi2006千年虫 附录参考书籍与相关材料

1.动态规划的动机和基本思想 首先声明,这里所说的动态规划的动机是从竞赛角度出发的动机。 1.1 解决重复子问题 对于很多问题,我们利用分治的思想,可以把大问题分解成若干小问题,然后再把各个小问题的答案组合起来,得到大问题的解答。这类问题的共同点是小问题和大问题的本质相同。很多分治法可以解决的问题(如quick_sort,hanoi_tower等)都是把大问题化成2个以内的不相重复的小问题,解决的问题数量即为∑(log2n / k)。而考虑下面这个问题: USACO 1.4.3 Number Triangles http://122.139.62.222/problem.php?id=1417 【题目描述】 考虑在下面被显示的数字金字塔。 写一个程序来计算从最高点开始在底部任意处结束的路径经过数字的和的最大。每一步可以走到左下方的点也可以到达右下方的点。 7 3 8 8 1 0 2 7 4 4 4 5 2 6 1 在上面的样例中,从7到3到8到7到5的路径产生了最大和:30。 【输入格式】 第一个行包含R(1<= R<=1000) ,表示行的数目。后面每行为这个数字金字塔特定行包含的整数。所有的被供应的整数是非负的且不大于100。 【输出格式】 单独的一行包含那个可能得到的最大的和。 【样例输入】 5 7 3 8 8 1 0 2 7 4 4 4 5 2 6 1 【样例输出】 30 显然,我们同样可以把大问题化成小问题来解决。如样例中最底层的6就可以从次底层

Poj动态规划

[1]POJ 动态规划题目列表 容易: 1018, 1050, 1083, 1088, 1125, 1143, 1157, 1163, 1178, 1179, 1189, 1208, 1276, 1322, 1414, 1456, 1458, 1609, 1644, 1664, 1690, 1699, 1740(博弈), 1742, 1887, 1926(马尔科夫矩阵,求平衡), 1936,1952, 1953, 1958, 1959, 1962, 1975, 1989, 2018, 2029,2039, 2063, 2081, 2082,2181, 2184, 2192, 2231, 2279, 2329, 2336, 2346, 2353,2355, 2356, 2385, 2392, 2424, 不易: 1019,1037, 1080, 1112, 1141, 1170, 1192, 1239, 1655, 1695, 1707,1733(区间减法加并查集), 1737, 1837, 1850, 1920(加强版汉罗塔), 1934(全部最长公共子序列), 1937(计算几何), 1964(最大矩形面积,O(n)算法), 2138, 2151, 2161(烦,没写), 2178, 推荐: 1015, 1635, 1636(挺好的), 1671, 1682, 1692(优化), 1704, 1717, 1722, 1726, 1732, 1770, 1821, 1853, 1949, 2019, 2127, 2176, 2228, 2287, 2342, 2374, 2378, 2384, 2411 状态DP 树DP 构造最优解 四边形不等式 单调队列 1015 Jury Compromise 1029 False coin 1036 Gangsters 1037 A decorative fence 1038 Bugs Integrated, Inc. 1042 Gone Fishing 1050 To the Max 1062 昂贵的聘礼 1074 Parallel Expectations 1080 Human Gene Functions 1088 滑雪 1093 Formatting Text 1112 Team Them Up! 1141 Brackets Sequence 1143 Number Game 1157 LITTLE SHOP OF FLOWERS 1159 Palindrome

动态规划和贪心的区别

动态规划和贪心算法的区别 动态规划法的基本思路: 动态规划是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推的方式去解决。此算法常用于求解某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案,消除递归过程中产生的大量重叠子问题。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。 贪心算法的基本思想: 在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,贪心算法所得出的解是一系列局部最优的选择。 把求解的问题分成若干个子问题,对每一子问题求解,得到子问题的局部最优解,把子问题的解局部最优解合成原来解问题的一个解。为了解决问题,需要寻找一个构成解的候选对象集合,起初,算法选出的候选对象的集合为空。接下来的每一步中,根据选择函数,算法从剩余候选对象中选出最有希望构成解的对象。如果集合中加上该对象后不可行,那么该对象就被丢弃并不再考虑;否则就加到集合里。每一次都扩充集合,并检查该集合是否构成解。 由以上可知:在贪心算法中,作出的每步贪心决策都无法改变,因为贪心策略是由上一步的最优解推导下一步的最优解,而上一部之前的最优解则不作保留。并且,每一步的最优解一定包含上一步的最优解。 而在动态规划算法中,全局最优解中一定包含某个局部最优解,但不一定包含前一个局部最优解,因此需要记录之前的所有最优解。动态规划的关键是状态

基于连通性状态压缩的动态规划问题

基于连通性状态压缩的动态规划问题 长沙市雅礼中学陈丹琦 【摘要】 基于状态压缩的动态规划问题是一类以集合信息为状态且状态总数为指数级的特殊的动态规划问题.在状态压缩的基础上,有一类问题的状态中必须要记录若干个元素的连通情况,我们称这样的问题为基于连通性状态压缩的动态规划问题,本文着重对这类问题的解法及优化进行探讨和研究.本文主要从动态规划的几个步骤——划分阶段,确立状态,状态转移以及程序实现来介绍这类问题的一般解法,会特别针对到目前为止信息学竞赛中涌现出来的几类题型的解法作一个探讨.结合例题,本文还会介绍作者在减少状态总数和降低转移开销两个方面对这类问题优化的一些心得. 【关键词】 状态压缩连通性括号表示法轮廓线插头棋盘模型

【目录】 【序言】 (3) 【正文】 (5) 一. 问题的一般解法 (5) 【例1】Formula 1 (5) 问题描述 (5) 算法分析 (5) 小结 (11) 二. 一类简单路径问题 (12) 【例2】Formula 2 (15) 问题描述 (15) 算法分析 (15) 小结 (16) 三. 一类棋盘染色问题 (17) 【例3】Black & White (17) 问题描述 (17) 算法分析 (17) 小结 (19) 四. 一类基于非棋盘模型的问题 (20) 【例4】生成树计数 (20) 问题描述 (20) 算法分析 (20) 小结 (21) 五. 一类最优性问题的剪枝技巧 (22) 【例5】Rocket Mania (22) 问题描述 (22) 算法分析 (23) 小结 (25) 六.总结 (25) 【参考文献】 (26) 【感谢】 (26) 【附录】 (26)

项目系统动态数据准备规划方案.doc

___________项目系统动态数据 准备方案 客户项目经理: 日期: 用友项目经理: 日期: 概要 系统上线前期,需要进行静态数据和动态数据的导入。静态数据和动态数据的导入,需要 提前按照一定的规则来整理数据。整理完毕的数据,将按照上线时的导入计划估算录入(或装载)的工作量,按照约定的时间进度导入产品系统。 静态数据的准备方式,见《静态数据准备方案》。 动态数据,是一种始终处于变动中的数据。例如客户的各种单据,库存数据,财务数据等 等。 所以动态数据的准备,必须集中时间,按照一定的格式,快速整理完毕,快速导入系统, 才 可能不会影响系统的正常上线。 一般动态数据的整理,在会计期末进行。动态数据的导入,在期初进行。 例如: 客户公司在 6 月 10 日进行库存盘点准备,20 日组织人员进行一次库存盘点,27 日前完成库存动态数据准备工作,28 日到 30 日进行库存数据的录入。则库存数据为截至到27 日的库存数据。 在盘点结束,恢复正常生产后,各相关部门人员应做好生产、采购、销售订单及库存变化 单据的保存、记录,在 6 月底(用调整单开帐)或7月期初(用开帐程序开帐)库存数据录入后, 开始补录入这批单据(必要时加班进行),在系统切换后 2 天内务必完成。 二、前期工作的准备

1、库存动态数据的准备 库存动态数据,应该按照现有库存帐的实时数据进行整理归类。建议在库存数据导入系统 之前,对库存数据进行盘点,得到真实的库存数据,并导入系统。否则,库存数据不准确,整个 系统的资料信息将毫无意义。 下表为库存动态资料准备表单,客户公司需拟定更详细的库存盘点计划,保证按期完成盘 点及动态数据准备工作。 库存期初余额 仓库代号期初时间年月 料品代号料品名称批号期初数据期初辅助量期初金额现存量辅助量

NOIP2017提高组初赛试题及答案

NOIP2017提高组初赛试题及答案 一、单项选择题(共15 题,每题1.5 分,共计22.5 分;每题有且仅有一个正确选项) 1. 从( )年开始,NOIP 竞赛将不再支持Pascal 语言。C A. 2020 B. 2021 C. 2022 D. 2023 2.在8 位二进制补码中,10101011 表示的数是十进制下的( )。B A. 43 B. -85 C. -43 D.-84 3.分辨率为1600x900、16 位色的位图,存储图像信息所需的空间为( )。A A. 2812.5KB B. 4218.75KB C. 4320KB D. 2880KB 4. 2017年10月1日是星期日,1949年10月1日是( )。C A. 星期三 B. 星期日 C. 星期六 D. 星期二 5. 设G 是有n 个结点、m 条边(n ≤m)的连通图,必须删去G 的( )条边,才能使得G 变成一棵树。A A.m–n+1 B. m-n C. m+n+1 D.n–m+1 6. 若某算法的计算时间表示为递推关系式:T(N)=2T(N/2)+NlogN T(1)=1 则该算法的时间复杂度为( )。C A.O(N) B.O(NlogN) C.O(N log2N) D.O(N2) 7. 表达式a * (b + c) * d的后缀形式是()。B A. abcd*+* B. abc+*d* C. a*bc+*d D. b+c*a*d 8. 由四个不同的点构成的简单无向连通图的个数是( )。C A. 32 B. 35 C. 38D. 41 9. 将7个名额分给4个不同的班级,允许有的班级没有名额,有( )种不同的分配方案。D A. 60 B. 84 C. 96 D.120 10. 若f[0]=0, f[1]=1, f[n+1]=(f[n]+f[n-1])/2,则随着i的增大,f[i]将接近与( )。B A. 1/2 B. 2/3 D. 1 11. 设A和B是两个长为n的有序数组,现在需要将A和B合并成一个排好序的数组,请问任何以元素比较作为基本运算的归并算法最坏情况下至少要做( )次比较。D A. n2 B. Nlogn C. 2n D.2n-1 12. 在n(n>=3)枚硬币中有一枚质量不合格的硬币(质量过轻或质量过重),如果只有一架天平可以用来称重且称重的硬币数没有限制,下面是找出这枚不合格的硬币的算法。请把a-c三行代码补全到算法中。 2. 将A中硬币分成X,Y,Z三个集合,使得|X|=|Y|=k, |Z|=n-2k 3. if W(X)≠W(Y) //W(X), W(Y)分别为X或Y的重量 4. then_______ 5. else_______ 6. __________ 7. if n>2 then goto 1 8. if n=2 then 任取A中1枚硬币与拿走硬币比较,若不等,则它不合格;若相等,则A中剩下的硬币不合格 9. if n=1 then A中硬币不合格 正确的填空顺序是( )。D A. b,c,a B. c,b,a C. c,a,b D.a,b,c 13. 在正实数构成的数字三角形排列形式如图所示,第一行的数为a11;第二行的数从左到右依次为a21,a22;…第n行的数为 an1,an2,…,ann。从a11开始,每一行的数aij只有两条边可以分别通向下一行的两个数a(i+1)j和a(i+1)(j+1)。用动态规划算法找出一条从a11向下通到an1,an2,…,ann中某个数的路径,使得该路径上的数之和达到最大。 令C[i,j]是从a11到aij的路径上的数的最大和,并且C[i,0]=C[0,j]=0,则C[i,j]=( )。A A. max{C[i-1,j-1],C[i-1,j]}+aij B. C[i-1,j-1]+c[i-1,j] C. max{C[i-1,j-1],C[i-1,j]}+1 D. max{C[i,j-1],C[i-1,j]}+aij 14. 小明要去南美洲旅游,一共乘坐三趟航班才能到达目的地,其中第1个航班准点的概率是0.9,第2个航班准点的概率为0.8,第3个航班准点的概率为0.9。如果存在第i个(i=1,2)航班晚点,第i+1个航班准点,则小明将赶不上第i+1个航班,旅行失败;除了这种情况,其他情况下旅行都能成功。请问小明此次旅行成功的概率是( )。D

动态规划算法在管理会计中的应用_钟方源

一、应用动态规划算法的动机 案例1:一家公司现有m万元,本年可投资的业务有n 项。其中,第一项业务对应的成本是C1,收益是R1;第二项业务对应的成本是C2,收益是R2……第n项业务对应的成本是C n,收益是R n。假设同种业务只能投资一次,不可重复。若每项业务对应的成本和收益已知,求该公司用m万元投资这些业务可以取得的最大收益。 显然可以用递归法来解决此问题: 设V(i,j)表示能够用j万元购买的前i项业务收益最大的子集的收益。根据V(i,j)这个最佳子集中是否包含业务i,可以得到下列递归关系式: V(i,j)= 该方程表示的是:如果第i项业务的成本C i比j小(或等于j),那么取得最大收益的方案有可能包含该项业务,至于是否包含,就看包含该业务所能取得的最大收益与不含该业务所能取得的最大收益两者中何者更大。而如果第i项业务的成本C i比j大,那就不能选择购买该项业务,也就是说取得最大收益的方案一定不包含此项业务。 当i和j有至少一项等于零时,表示该公司投资业务的资金为零或者不投资任何业务,所以其所能取得的收益一定为零,即当i=0或j=0时,V(i,j)=0,故该方程的边界条件是: 该案例目标是求出V(n,m),即用m万元购买的n项业务收益最大的子集的收益。 因此,可以得到以下递归过程(伪代码): function V(i,j:integer); begin if(i=0)or(j=0)then return0 else begin if j-C i>=0then Answer:=max[V(i-1,j),R i+V(i-1,j-C i)]; if j-C i<0then Answer:=V(i-1,j); end; end; 1.记忆化搜索。上面的递归算法显然是正确的,但是它的运算速度却很慢,因为它的时间复杂度是指数级的。如果记V(i,j)的值是d[i,j],以数组来表示,由于1≤i≤n,1≤j≤m,所以一共只有O(n×m)个d值需要计算,而在执行递归算法的时候却做了大量的重复运算。 可以这样改进这个算法: 因为V(i,j)的值一旦被计算出来就不会改变,所以在每次调用V函数之前先检查之前是否已经计算过该值,如果是,则直接从数组中读出,不必再花费时间重新进行计算,即: function V(i,j:integer); begin if Calculated[i,j]then return d[i,j]; //此处为原来的V函数代码 d[i,j]:=Answer; Calculated[i,j]:=true; end; 2.自底向上的递推。除了上述这种改进方法,还有另外一种改进方法:可以按照一定的顺序计算所有的d值。由于计算d[i,j]需要知道d[i-1,j]和d[i-1,j-C i],所以可以按照i~ j递增的顺序来计算d[i,j],即: 动态规划算法在管理会计中的应用 【摘要】动态规划算法是运用状态转移解决多阶段决策的一种最优化方法。这种方法基于最优化原理,把多阶段过程转化为一系列单阶段问题,进而逐个求解,可以高效地解决许多用贪心算法或分治算法无法解决的问题。本文结合管理会计中的问题,运用运筹学、金融建模等知识,探讨了动态规划算法在管理会计中的应用。 【关键词】动态规划;管理会计;最优化原理 【中图分类号】F230【文献标识码】A【文章编号】1004-0994(2016)05-0053-3 钟方源 max{V(i-1,j),R i+V(i-1,j-C i)},j-C i≥0 V(i-1,j),j-C i<0 V(0,j)=0,j≥0 V(i,0)=0,i≥0 2016.05财会月刊·53·□ 财务·会计□

相关文档
最新文档