DP在ACM的算法里面可算是重中之重,题目类型千变万化,题目难度差异也很大.是一种很讲究技巧的算法,而且代码实现相对容易,1y率非常高(除有些bt数据外).总之DP就是一向非常重要,又非常博大精深的算法.我们学校的Roba的大牛在这方面就有很深的造诣.
  说一下自己这几天接触的初级DP,DP中最重要的往往是状态和状态之间的转移,找到状态转移方程,用递归或者是递推的方式列出方程,题目也就迎仞而解了,而所谓的难题往往是初看不知所措,找不出状态转移方程,或者根本连什么是状态的都看不清,下面分析一下有关DP的非常经典的3个问题:
  1.求局部最大和
  大意是这样给你一个数组,在其中取任意连续多个,使其和要最大.假设这个数组大小是10,一般会想到怎么做呢,一个连续的序列,肯定能被一个st和end唯一的表示出来,遍历所有的可能,求出其中最大的即可.

以下内容为程序代码:
for(st=0;st<n;st++)for(end=st;end<n;end++)for(i=st;i<=end;i++)max+=n[i];

一个3重循环,耗时是10^3,可以在不改变算法思想的基础上加以优化吗,答案是肯定的.
建立一个sum[10]数组表示从数组n的累加和,这样的话每次计算max只需要一次操作sum[end]-sum[st-1]了,至于sum数组的建立过程,只需要一个O(n)的时间即可做到,所以总的时间复杂度降为O(10^2);能把复杂度将到O(n)吗?这时候就要用到动态规划的思想了.假设当前状态是在以end结尾的局部最大和,通过这个能否得到以end+1结尾的局部最大和.想一想,以end+1结尾,可以分为两部分,一个是只有end+1这一个元素,另外就是再加上以end结尾的局部最大和,说一般了,就是从先前状态最优推出了当先的最优态,具体状态转移方程就是:

以下内容为程序代码:
if(ans[i]<0)ans[i+1]=n[i+1];else ans[i+1]=ans[i]+n[i+1];

再把遍历所有的ans,找出最大的就OK了,这样的话只需要O(n)的时间就能完成.
TOJ1782有道类似的题目,感兴趣的同学可以去看看.
  2.最长递增子序列
  大意是在一个序列中找到一个最长的递增子序列,求出其长度.例如1 7 3 5 9 4 8 Ans: 4 (1 -> 3 -> 5 -> 8),咋看之下,好象没有合适的做法.遍历?2^n的复杂度,显然是不可行的.通过上面例题,我们可以考虑用DP的方法来解决这类问题.假设一个长度为n的数组,现在已经知道以从1到i为序列的尾的最大递增子序列,那么要求以i+1为尾的序列的最大递增子序列,只要判断n[i+1]是否大于a[1:i],然后取其中ans[i]最大的那个加1就是按ans[i+1]的值.最后在遍历所有的ans找出最大的即可.是不是和局部最大和很想象,唯一的不同就在于在局部最大和中只需要ans[i]即可求出ans[i+1],这是因为n[i+1]的前面只能接n[i]导致的,而这题的ans[i+1]确是由ans[1:i]共同决定的,这是由于最长递增子序列可以不连续导致的.换句话说,要是这题是求最长递增连续子序列的话,那就和上题的处理几乎一样了.说到这里,相信大家对DP都有了一个基本的了解了,我的理解是对于有些不知如何下手的题目,确定一个先前状态往往是给了一个已知条件,是问题明朗化了,再找到当前状态,所以说DP的难点在于状态划分和确定状态转移方程,而我的感觉是往往前者更重要.回到刚才那个题目,内层循环要做的是找到一个小于n[i+1]的并且长度最长的子序列,既然是查找,那能否找到O(logn)查找复杂度呢.答案是肯定的.具体怎么实现,大家可以查看这个网址:最长递增子序列的高效算法.上面说的很详细,我也理解的不好.
  第三个问题下次再说吧,睡觉去了...
接着上次的内容,第三个经典例题:最长公共子串;
      大意是:有两个字符串A,B,另有一字符串C,若C同时是A,B的子串,则C为公共子串,求出长度最长的公共子串的长度.
      ans[i][j]=0;//if(i==0||j==0)(1)
      ans[i][j]=ans[i-1][j-1];//if(A[i]==B[j])(2)
      ans[i][j]=Max(ans[i][j-1],ans[i-1][j])//if(A[i]!=B[j])(3)
分析一下(2),(3),对于(2),如果A[i]==B[j],显然ans[i][j]要加1,那么会不会加2呢?如果加2的话,那么说明A[i],B[j]分别为公共子串两个不同的字符,那么必然有前后之分,前面的那个字符已经对应了一个字符串的尾部,另外一个字符则不可能是公共子串了,所以对于A[i]==B[j],ans[i][j]加且仅加1.对于(3)采取同样的分析,易知其分别减去A,B尾部的字符,必有一长度不变.
      具体的代码实现可以有两种方式.1.采取递推的实现方式,自底向上采取两重循环,时间复杂度为O(n^2),不过不是所有的状态转移方程都可以这样做,后面会详细分析.2.自顶向下,逐级递归,但要注意递归层数(据说不能超过7600层),一般情况下都伴随这记忆化,这样能使时间复杂度从指数级别降低到O(n^2)的级别.至于和递推在时间上的优劣关系,或者说有没有题用其中一种会TLE而另一种会AC,就不清楚了,望达人指教.
      总结一下这三道经典例题.虽然是属于入门级的简单DP,但还是能给我们一点启示的.如何划分状态?有些时候,状态的划分不能仅仅根据答案,还应该加上某些限制条件,这样的话会更有利于状态的转移.像求局部最大和中ans[i]并不是前i个数列中的局部最大和值,还加上了该局部最大和的尾部必须是n[i],最长递增子序列也是一样.还有一点是状态的转移条件,有些时候只有一个前驱,而有的时候又有多个前驱,像2.(这也是2.的复杂度要比1.高的原因),这个要注意.关于这3道题,还有许多的类似问题,像多维局部最大和,最大m子段和等等.TOJ1564,POJ2479,TOJ1633都是相应的题目,感兴趣的同学可以做一下.     
      回到刚才所说的,什么时候能用递推,什么时候又不能呢?
      先看一下下面这道例题,POJ1088
      大意是:给一个M*N的矩阵,每一点附上一个值h,要求求一条路径,路径上的相邻两点必须在矩阵中前后左右相邻,并且前驱的h值高于后继.很容易想到状态转移方程:l[i][j]=Max(l[i][j+1],l[i][j-1],l[i+1][j],l[i-1][j])+1//if(h[i][j+1]>h[i][j]...),那么用递归写的话只需
if(map[i][j]>map[i][j+1])
if(flag[i][j+1]==-1)
a=flag[i][j+1]=search(i,j+1);
else
a=flag[i][j+1];
if(map[i][j]>map[i][j-1])
if(flag[i][j-1]==-1)
b=flag[i][j-1]=search(i,j-1);
else
b=flag[i][j-1];
if(map[i][j]>map[i+1][j])
if(flag[i+1][j]==-1)
c=flag[i+1][j]=search(i+1,j);
else
c=flag[i+1][j];
if(map[i][j]>map[i-1][j])
if(flag[i-1][j]==-1)
d=flag[i-1][j]=search(i-1,j);
else
d=flag[i-1][j];
return Max(a,b,c,d)+1;
      要是用递推怎么写呢,好象不好写吧,结合前面的3个例题(都是用递推写的),可以得出满足写递推式的两个条件:1.起点必须确定,象3.中ans[i][j]=0//if(i==0||j==0)是现而易见的,而此题在不同的输入下结果是不同的.2.每一点在推出时都能确保其前驱已被推出,在3.中,在二重循环中,明显在计算ans[i][j]中ans[i-1][j],ans[i][j-1],ans[i-1][j-1]已经确定了,但是此题却不能保证.
      好了,说了这么多,相信大家对DP都有一个大致的印象了,划分好状态,找出状态转移方程,看能否用递推,不能的话就递归,但记住要记忆化哦.但是有一类题,和DP很像,但却很难找到转移方程,或者说起状态转移方程只不过是一个遍历的过程.对于这种题只要不和DP弄混,还是很好做的.
      ZOJ1687大意是:一堆石头m个.两队人轮流取石头,每人取的上限都给出,至少要取一个,谁最后取完谁就失败,要你判断哪队能赢,咋看之下,确实没有状态转移,其实根本不用这么复杂,对于第i个人还剩下j块石头直接
for (i=1;i<=a[p];i++)
if (tot - i > 0 && solve(tot-i,next) == 0)
{
   ans[tot][p] = 1;
   return 1; //如果取子后对方会输,则返回1
}
完全是暴力嘛,就这么简单!时间复杂度为O(石头数*人数),对于n队,应该也有同样的做法.

DP的学习的更多相关文章

  1. DP动态规划学习笔记——高级篇上

    说了要肝的怎么能咕咕咕呢? 不了解DP或者想从基础开始学习DP的请移步上一篇博客:DP动态规划学习笔记 这一篇博客我们将分为上中下三篇(这样就不用咕咕咕了...),上篇是较难一些树形DP,中篇则是数位 ...

  2. 概率DP入门学习QAQ

    emmmm博客很多都烂尾了...但是没空写..先写一下正在学的东西好了 概率DP这东西每次考到都不会..听题解也是一脸懵逼..所以决定学习一下这个东东..毕竟NOIP考过...比什么平衡树实在多了QA ...

  3. DP动态规划学习笔记

    作为考察范围最广,考察次数最多的算法,当然要开一篇博客来复习啦. 子曰:温故而知新,可以为师矣 我复习DP时有一些自己对DP的理解,也就分享出来吧. ——正片开始—— 动态规划算法,即Dynamic ...

  4. 树形DP入门学习

    这里是学习韦神的6道入门树形dp进行入门,本来应放在day12&&13里,但感觉这个应该单独放出来好点. 这里大部分题目都是参考的韦神的思想. A - Anniversary part ...

  5. 咸鱼的ACM之路:动态规划(DP)学习记录

    按挑战程序设计竞赛介绍的顺序记录一遍学习DP的过程. 1. 01背包问题 问题如下: 有N个物品,每个物品(N[i])都有一定的体积(W[i]),和一定的价值(V[i]) 现在给定一个背包,背包的容量 ...

  6. 关于数位DP的学习

    ---恢复内容开始--- 因为最近做比赛经常会出现数位DP,便尝试着去学学看数位DP. 先给出两篇论文的链接: <数位计数问题解法研究> <浅谈数位类统计问题> 然后也是寻找了 ...

  7. [斜率优化DP]【学习笔记】【更新中】

    参考资料: 1.元旦集训的课件已经很好了 http://files.cnblogs.com/files/candy99/dp.pdf 2.http://www.cnblogs.com/MashiroS ...

  8. 区间DP的学习(持续更新)

    例题: 1.Multiplication Puzzle 原题地址:http://poj.org/problem?id=1651 2.Dire Wolf 原题地址:http://acm.split.hd ...

  9. 清北学堂2018DP&图论精讲班 DP部分学习笔记

    Day 1 上午 讲的挺基础的--不过还是有些地方不太明白 例1 给定一个数n,求将n划分成若干个正整数的方案数. 例2 数字三角形 例7 最长不下降子序列 以上太过于基础,不做深入讨论 例3 给定一 ...

随机推荐

  1. 贝叶斯---最大似然估计(高翔slam---第六讲 )

    1.贝叶斯---最大似然估计 回顾一下第二讲的经典SLAM模型: 通过传感器(例如IMU)的运动参数u来估计运动(位姿x)[定位],通过相机的照片的观测参数z来估计物体的位置(地图y)[建图],都是有 ...

  2. 【转】Cron表达式详解

    Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式: (1) Seconds Minutes Hours DayofMonth Mo ...

  3. dtruss

    一.简介 系统调用跟踪工具.   二.实例

  4. xpath&css选择器

    本文参考较多,原创基本没有,权当知识归纳. xpath并不复杂,简单的使用看完之后,及时查阅文档也是可以写出来的. 这里放上我的练手文件,大家可以参考,或者挑毛病(*^__^*) 嘻嘻-- xpath ...

  5. redis在游戏服务器中的使用初探(一) 环境搭建

    这里我们尝试在游戏服务器中的数据处理中使用redis 通过该系列文章能够学习 redis的基本操作 源码编译 客户端开源库的编译和使用 以及在游戏服务器中的缓存使用 作为初次摸索 尽量使得环境简单  ...

  6. selenium中动作链的使用

    一.问题 我们有时候在使用selenium的时候,会遇到悬停后点击元素的操作,因此需要一个动作链来完成这个功能. 二.解决 从selenium的包中导入actionchains函数,利用xpath找到 ...

  7. Mybatis的针对于同一个有自己父类或子类的递归查询 (如商品分类)

    1.pojo代码 private Integer categoryId; private Integer superId; private String name; private Byte leve ...

  8. POJ 1328 Radar Installation 贪心 A

    POJ 1328 Radar Installation https://vjudge.net/problem/POJ-1328 题目: Assume the coasting is an infini ...

  9. MD5盐值加密

    加密思路 思路解析:(数据解析过程基于16进制来处理的,加密后为16进制字符串) 加密阶段: 对一个字符串进行MD5加密,我们需要使用到MessageDigest(消息摘要对象),需要一个盐值(sal ...

  10. 关于使用Visual编译静态库动态库及其使用的问题

    本文主要讲述了如何使用Visual Studio 2013 编译静态库和动态库,并使用. 一.静态库 1.  编写静态库 若要创建将引用并使用刚创建的静态库的应用程序,请从“文件”菜单中选择“新建”, ...