zoukankan      html  css  js  c++  java
  • 线性DP 学习笔记

    前言:线性DP是DP中最基础的。趁着这次复习认真学一下,打好基础。

    ------------------

    一·几点建议

    1.明确状态的定义

    比如:$f[i]$的意义是已经处理了前$i个元素,还是处理第$i$个元素?这对于后期的调试非常重要。

    2.明确边界状态

    比如:$f[0]$是等于$0$还是等于$1$又或是$f[0]=a[1]$?当然,只有明确了状态的定义,才能明确边界。

    3.明确转移

    要想明白转移的顺序。例如完全背包和01背包,其不同就在于$j$的枚举顺序不同。这不得不让人注意。

    另一方面,DP的转移有两种写法,以线性DP为例:

    1.要算$f[i]$,在$i$之前找转移:$f[i]=max/min(f[j]+value)$。

    2.算完$f[i]$,用$f[i]$更新后面状态,$f[j]=max/min(f[i]+value)$。

    根据问题的特殊性,灵活转换思路。

    4.有时候DP可以转化成记忆化搜索

    当冗余状态比较多时,可以用记忆化搜索,降低常数。

    二.正题

    给定一个序列$a_{i}$。求序列的最长上升子序列。

    1.暴力枚举。不难得出状态转移方程:$f[i]=max(f[j]+1,f[i])$(当$jleq i$并且$a[j]<a[i]$)时间复杂度$n^2$。

    2.单调栈。设$f[i]$为长度为$i$的序列的最小末尾数值。

    这种方法类似于贪心。很好理解,如果末尾的数值越小,那么更有可能把序列中后面的数加进来。时间复杂度$nlogn$。

    -----------------------------

    求两个串的最长公共子序列。

    我们定义$f[i][j]$为第一个串前$i$个,第二个串前$j$个的最长公共子序列长度。

    如果$a[i]=b[j]$,那么有$f[i][j]=max(f[i][j],f[i-1][j-1]+1)$。

    如果不相等,考虑继承:$f[i][j]=max(f[i-1][j],f[i][j-1])$。

    ------------------------------------

    求一个序列的最大子段和。

    1.暴力:$O(n^2)$。枚举左右端点。

    2.分治:$O(nlogn)$。我一般用线段树实现。

    3.动态规划。时间复杂度$O(n)$。设$f[i]$为从$i$开始向前延伸的最大子段和。则有$f[i]=max(f[i-1]+a[i],a[i])$。边界$f[1]=a[1]$。

    -----------------------------------------

    给定一个序列,将其划分成不超过$k$个子区间,最小化每个区间和的最大值。

    我们设$f[i][j]$为已经处理了前$i$个元素,划分成$j$个区间的最大值。

    1.暴力:时间复杂度$n^3$。有转移$f[i][j]=max(f[k][j],sum_{i=k+1}^n a[i])$。

    2.二分答案:刷表,看值是否都小于$mid$。

    3.转化成“可行性解”。设$f[i]$为前$i$个数最少划分成多少段是合法的。则有$f[i]=min(f[j]+1|sum_{k=j+1}^i a[k]<mid)$。

    --------------------------------------------

    后记:这里讲的都是模板,做题的时候还是要自己学会转化。有时候对于题面不清楚可以自己试着模拟一下,一般都能找到转移方法。

  • 相关阅读:
    js正则表达语法
    Codeforces 976E/925C (01Trie树)
    ZOJ 3879(大模拟)
    CF967C(二分+细节)
    CF967A(细节模拟)
    HDU 2222(AC自动机模板)
    HDU 5510(KMP+思维)
    HDU 6273(树状数组+思维)
    HDU 6266(思维+规律)
    HDU 6264(思维)
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/12656063.html
Copyright © 2011-2022 走看看