zoukankan      html  css  js  c++  java
  • 【学习笔记】DP 优化

    目录

    1. ds 优化
    2. 单调队列优化
    3. 斜率优化
    4. 决策单调性优化

    1. ds 优化

    考虑枚举右端点 (i),求出 (sumlimits_{j=1}^if(j,i))

    假设我们维护了一个序列,(j) 位置存的是 ([j,i]) 的答案。

    先预处理出 (lst_i),表示 (i) 往左数第一个等于 (a_i) 的数的下标。

    如果右端点从 (i-1) 变成 (i),发现对于所有 (j~(lst_i+1<jle i)),它们的答案都要加 (1)。而对于 (j~(jle lst_i)),由于 (a_i) 已经在 (lst_i) 上出现过,答案不变。

    本题还要求出平方和,用线段树维护即可。

    枚举建几个基站,建 (t) 个基站的答案可以从 (t-1) 转移过来。

    (f_i) 表示在第 (i) 给村庄建一个基站,已经考虑了 ([1,i-1]) 内的村庄的补偿,最少需要多少钱。

    如果我们要求 (f_i),维护一个线段树,(j) 位置表示上一个基站在 (j) 村庄时(即从 (j) 转移),(f_i) 的值。

    预处理 (l_i,r_i) 表示坐标在 ([d_i-s_i,d_i+s_i]) 内的最左边和最右边的村庄。

    (f_i) 是很好求的,只需要求个最小值。考虑如何更新这个序列。

    如果有一个村庄 (k)(r_k=i),那么当我们求 (f_{i^prime}~(i^prime>i)) 时,如果从 (j~(j<l_k)) 转移,那么覆盖不到 (k),需要支付 (w_k)

    用 vector 或前向星,在 (i) 位置存所有 (r_k=i)(k)。每次求出 (f_i) 时,枚举这里面的 (k),在 ([1,l_k-1]) 上加上 (w_k)

    求出所有 (f) 后,重建线段树,把 (f_i) 放到 (i) 位置上,用于求建 (t+1) 个基站的答案。

    练习题

    2. 单调队列

    (f_i=minlimits_{d_ile jle i}{a_i}),其中 (d_i) 单调不减。

    显然可以 st 表 (O(nlog n)),但有个 (d_i) 单调不减的性质没用上。

    考虑维护一个队列,求 (f_i) 时,里面从小到大存了所有在 ([1,i-1]) 内的下标。

    但这里面有很多元素是没有用的。

    (forall j,kin[1,i-1],j<k)

    1. (a_jge a_k),考虑任意一个能被 (j) 更新到的 (i~(i>k))(即 (d_ile j)),必有 (d_ile j<k)(k) 也能更新 (i)。而 (a_jge a_k)(j) 并不优于 (k),没必要存在于队列中。

    2. (j<d_i)(j) 无法更新 (i),而随着 (i) 增大 (d_i) 不会减小,即 (d_i) 再也没有用了。

    综上,这个队列应时刻满足:

    1. 下标递增
    2. (a_j) 递增
    3. 队首 (ge d_i)

    (f_i) 时只需取出队首,因为它已经是最小的了。然后从队尾删除直到队尾的值 (<a_i),插入 (i)

    容易发现每个元素最多进、出各一次,所以时间复杂度 (O(n))

    练习题

    3. 斜率优化

    (c) 做前缀和。设 (f_i) 表示以第 (i) 个玩具作为一个容器的最后一个玩具,前 (i) 个玩具的最小费用。

    (f_i=minlimits_{1le j<i}{f_j+(j-i-1+c_i-c_j-L)^2})

    若令 (L)(1),再令 (c_k) 加上 (k),方程变成 (f_i=minlimits_{1le j<i}{f_j+(c_i-c_j-L)^2})

    但它并不能用单调队列,因为展开后同时含有 (c_i imes c_j)(c_j^2)

    考虑计算 (f_i) 时,选择 (j,k~(j<k)) 作为转移,如果 (j) 优于 (k)

    (f_j+(c_i-c_j-L)^2<f_k+(c_i-c_k-L)^2)

    (f_j+c_j^2-2c_ic_j-2c_jL<f_k+c_k^2-2c_ic_k-2c_kL)

    ((f_j+c_j^2-2c_jL)-(f_k+c_k^2-2c_kL)<2c_i(c_j-c_k))

    (dfrac{(f_j+c_j^2-2c_jL)-(f_k+c_k^2-2c_kL)}{c_j-c_k}>2c_i)

    对于第 (t) 个决策,在平面直角坐标系内找一点 (P_t(c_t,f_t+c_t^2-2c_tL))。用 (k_{j,k}) 表示 (P_j)(P_k) 连成线段的斜率,令(K_i=2c_i)。那么对于 (i)(j) 优于 (k) 当且仅当 (k_{j,k}>K_i)

    仿照单调队列,我们维护一个队列,求 (f_i) 时,里面从小到大存了所有在 ([1,i-1]) 内的下标。

    这里面有很多点是没有用的。

    1. (forall x_1,x_2,x_1<x_2,k_{x_1,x_2}le K_i),此时 (x1) 不优于 (x_2)。因为 (c_i) 单调递增,(K_i) 也单调递增,对于 (i^prime~(i^prime>i))(x1) 仍不优于 (x_2)。所以 (x_1) 是没用的。
    2. (forall x_1,x_2,x_3,x_1<x_2<x_3,k_{x_1,x_2}ge k_{x_2,x_3}),对于任意的 (i)(k_{x_2,x_3}>K_i),则 (x_2) 优于 (x_3)。而此时必有 (k_{x_1,x_2}>K_i),即 (x_1) 优于 (x_2)。所以 (x_2) 是没用的。

    所以我们要维护一个斜率递增的下凸壳,每次看队首和队首的后一个元素的斜率,若 (le K_i) 弹出队首。不断重复直到斜率大于 (K_i),然后从队首转移到 (i)。不断弹出队尾直到队尾的前一个元素和队尾的斜率小于队尾和 (i) 的斜率。然后在队尾插入 (i)

    有些题目会出现不存在斜率的情况(即两点横坐标相同),此时要特判并返回 (infty)(-infty)(视具体情况而定)。


    刚刚我们解决了 (c_j) (即决策点横坐标)单调递增,(K_i) 也递增的情况。

    但有些题目决策点横坐标单调递增,但 (K_i) 不一定。此时刚才的第一条性质不一定满足,我们就不能从队首删点。此时我们就要在队列里二分,找到第一个点,满足它和下一个元素的斜率大于(或小于,视题目而定)(K_i),并把它作为决策点。

    有些题目甚至决策点横坐标和 (K_i) 都不单调,此时就要用平衡树或 CDQ 分治。

    练习题

  • 相关阅读:
    MongoDB Shell
    mongo 日记
    java 堆栈 静态
    面向对象(2)
    面向对象(1)
    mongo 学习笔记
    深入浅出学Spring Data JPA
    java记录
    mongodb 2.6 window 安装启动服务
    CF1012F Passports
  • 原文地址:https://www.cnblogs.com/creating-2007/p/14809415.html
Copyright © 2011-2022 走看看