• dp常见优化方法


    noip范围内的dp优化方法:

    加速状态转移

    1.前缀和优化

    2.单调队列优化

    3.线段树或树状数组优化

    精简状态
    3:精简状态往往是通过对题目本身性质的分析,去省掉一些冗余的状态。相对以上三条套路性更少,对分析能力的要求更高。

    前缀和优化

    逆序对(loj)

    求长度为n,逆序对为k的排列有多少个,答案对10^9^+7取模

    1<=n,k<=200;

    1<=n,k<=2000;

    我们从小到大依次把数字插入到排列中,以这个思路进行dp。

    dpi,j 表示插入了前i个数,产生的逆序对为j的排列的方案数;

    考虑转移第i+1个数插在哪里,因为它比之前的都要大,插在最后面就(dp[i+1][j+0]+=dp[i][j])表示不产生新的逆序对。

    如果是最后一个数前面就是(dp[i+1][j+1]+=dp[i][j]),表示产生一个逆序对,倒数第2个数前面就是(dp[i+1][j+2]+=dp[i][j]),依次类推:

    我们如果考虑dp[i][j]能从哪些状态转移过来,就可以前缀和优化。

    (dp[i][j]=sumlimits_{k=0}^{i-1}dp[i-1][j-k]=sum[i-1][j]-sum[i-1][j-1])

    (sum[i][j]=sumlimits_{k=0}^jdp[i][k];)

    单调队列维护

    基本形式,适用范围

    单调队列维护dp,一般就是把一个O(N)转移的dp通过单调队列优化成一个均摊O(1)转移的式子。
    式子形如: dp[i]=max{f(j) }+g[i](这里的g[i]是与j无关的量),且j的取值是一段连续区间, 区间端点的两端随着i增大而增大的区间。
    (同时如果这个可行的区间左端点固定,我们就可以通过之前讲的记录前缀最小值来处理)
    这里的f(j)是仅和j有关的项。以下是常见的一维和二维的情况。
    dp[i]=max{dp[j]+f(j)}+g[i] 或者 (dp[level][i])=max{(dp[level-1][j])+f(j)}+g(i)
    这样的题我们就可以做单调队列优化

    bzoj1855: 股票交易

    我们考虑在未来T天内的某只股票,第i天的买入价为每股APi,第i天的卖出价为每股BPi(对于每个i,都有APi>=BPi),每天不能无限制地交易,规定第i天至多只能购买ASi股,至多卖出BSi股。另外规定在两次交易(买入或者卖出均算交易)之间,至少要间隔W天,也就是说如果在第i天发生了交易,那么从第i+1天到第i+W天,均不能发生交易。还规定在任何时间,一个人的手里的股票数不能超过MaxP。
    初始w手里有一大笔钱(可以认为钱的数目无限),但是没有任何股票,问T天以后,小w最多能赚多少钱。
    0<=W<T<=2000

    (f[i][j])表示到第i天手里持有j的股票的最大收益,那么第i天有三种操作:

    不买不卖:(f[i][j]=f[i-1][j])

    买入:(f[i][j]=f[i-w-1][k]-ap[i]*(j-k)|k>=j-as[i])

    卖出:(f[i][j]=f[i-w-1][k]+bp[i]*(k-j)|k<=j+bs[i])

    对于买入,我们对其变形:(f[i][j]=f[i-w-1][k]-ap[i]*(j-k)=f[i-w-1][k]+ap[i]*k-ap[i]*j)

    那么可以用单调队列维护(f[i-w-1][k]+ap[i]*k)(因为对于固定的i,ap[i]是固定的),这样(f[i][j])就能做到O(1)求得,而不必枚举k。卖出也一样。

    bzoj3831: [Poi2014]Little Bird

    有一排n棵树,第i棵树的高度是Di。
    MHY要从第一棵树到第n棵树去找他的妹子玩。
    如果MHY在第i棵树,那么他可以跳到第i+1,i+2,...,i+k棵树。
    如果MHY跳到一棵不矮于当前树的树,那么他的劳累值会+1,否则不会。
    为了有体力和妹子玩, MHY要最小化劳累值。
    N(2<=N<=1 000 000)

    设f[i]为MHY跳到第i棵树的最小代价;

    f[i]=min(f[j]+1) i-k<=j<i;

    f[i]=min(f[j]) i-k<=j<i&&h[j]>h[i];

    发现上面那个东西用单调队列直接搞定,但下面那个不太好搞。不过发现由于h[j]>h[i]对答案的贡献至多为1,所以原来如果f[j]<f[j‘],那么算上h[j]和h[j’]的影响后j仍然不会比j‘更差,于是直接维护一个f递增的单调队列,其中当f相同的时候使h递减就行了。

    送你一堆区间

    送你在数轴上的 n 个区间和 m 个关键点, 你可以决定每个区间选或不选,问有多少种方案覆盖 所有的关键点.
    对 1000000009 取模.
    N<=5*10^5

    Noip2008传纸条

    一个m行n列的矩阵,而小渊和小轩传纸条。纸条从小渊坐标(1,1),传到小轩坐标(m,n)。从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。

    班里每个同学都可以帮他们传递,但只会帮他们一次。
    全班每个同学有一个好心程度。小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度之和最大。
    n,m<=300

    其实可以理解为从小渊到小轩传两次。
    最暴力的做法:设(dp[i][j][k][l])是第一张纸条到达(i,j),第二张到达(k,l)时最大权值。那么方程就是
    (dp[i][j][k][l] = map[i][j] + map[k][l] + max(dp[i - 1][j][k - 1][l],dp[i -1][j][k][l - 1],dp[i][j - 1][k -1][l],dp[i][j -1][k][l -1]);)

    还有一点注意的是,如果i == k && j == l,也就是第二张走了第一张的路径,那么就要减去, (dp[i][j][k][l] -= map[i][j];)
    接下来枚举每个i,j,k,l,最后输出(dp[m][n][m][n])就行了,但这样做时间复杂度是O(n^4)

    其实我们可以让两个路线并行走,同时走。
    而既然第一张与第二张是同时走,那么我们知道他们的步数是一样的,步数 = 横坐标+纵坐标,所以只需枚举i,j,k,就能计算出l,只需三重循环,时间就变成了O(n^3);
    $dp[i][j][k] = map[i][j] + map[k][i+j-k] + max(dp[i - 1][j][k - 1],dp[i -1][j][k],dp[i][j-1][k -1],dp[i][j -1][k]); $

  • 相关阅读:
    Nginx 使用 GeoIP 模块区分用户地区
    使用nginx转发tcp请求(解决访问内网的腾讯云redis)
    open file cache提升nginx性能
    使用 nginx-http-concat
    使用goaccess对Nginx日志简单分析
    Zookeeper系列一:Zookeeper基础命令操作
    k8s nginx应用-获取客户端访问真实IP
    mysql 备份数据库中的一张表
    ssh命令带密码
    Linux下grep显示前后几行信息
  • 原文地址:https://www.cnblogs.com/zhuier-xquan/p/11330107.html
Copyright © 2020-2023  润新知