zoukankan      html  css  js  c++  java
  • 斜率优化学习笔记

    在上一个单调队列 dp 优化中,总结了一个式子
    $$dp[i] = min(dp[j] + w(j)) + w_x(i)$$
    其中,一开始的 w 可以被分为 i , j 两个部分,只把 j 的部分放进单调队列即可,但往往,区间 dp 的式子中出现了如下的模样
    $$dp[i] = min(dp[j] + w(i) * w(j))$$
    可以发现 w ,难以被分开,斜率优化即使解决以上的优化方式。
    玩具装箱为例
    状态转移容易想出:
    $$dp[i] = min(dp[j] + (sum[i] - sum[j] + i - j - L - 1)^2)$$
    化简:

    $$sum[i] += i,sum[j] += j,L++$$
    出现了 i 项 * j 项的情况
    那么继续将式子进行化简可得
    $$dp[i] + 2 * sum[i] * (sum[j] + L) - s[i] ^ 2= dp[j] + (sum[j] + L) ^ 2$$
    如果我们把
    $$k = 2 * sum[i],b = dp[i] - s[i] ^ 2,y = dp[j] + (sum[j] + L)^ 2,x = (sum[j] + L)$$
    那么就有了
    $$y = kx + b$$
    观察我们的任务,求最小dp[i],而此时
    $$ sum[i] ^ 2 $$
    是固定的
    所以是求最小化的 b ,如果我把带选项根据(x,y)写入坐标系中,
    就是将斜率为 k 的线段上移,碰到的第一个点,即是最小的b。
    那么就讨论什么样的点才能被我第一个碰到
    考虑具体实现,知道 sum[i] 单调递增,x 也单调递增
    那么就实现一个存放点的序列,满足第一个点是我的最优点。
    有以下情况:
    1. 当序列中第一个点与第二个点组成的斜率 > k
    此时,必定可以取到第一个点。
    2. 与上面的情况相反
    此时,一定取不到第一个点,那么就把第一个点删去,后面也不可能取到
    那么做完这个点后,如何放进我这个序列中满足第一个点是最优决策呢

    由图片可知,组成上凸包时,一定无法取到中间那个点
    所以直接弹掉。
    发现这个序列不就是用单调队列维护么

    这种情况下,默认了k递增x递增。
    若 k 不递增,那么第一个就不能直接做,而是要二分第一个满足
    与前面的点的斜率 < k,与后一个点的斜率 > k 的点。
    代码 
    x 不递增,就将 k ,x 位置调换
    若 k ,x都不递增,平衡树维护吧
    例题:Cats Transports
    剥下这道题的外壳,让它变为一道裸的斜率优化。
    很容易想到状态,但复杂度显然过不去,也没有单调性,只能自己创造。

    $$c[i] = t - sum[i],sum[i] = sum_{j = 1} ^{i} d[j]$$
    如果出发时间为t,那么 t - c[i] 即是等待时间
    将 c 数组排序后,带走其中一个即可以带走旁边几个,那就是变成了连续选择,c排序后有了单调性,那么转移式就成了
    dp[k][i]表示第 k 个饲养员,到 i 这个地方取猫的最小代价
    $$dp[k][i] = min(dp[k - 1][j] + sum_{t = j + 1}^ic[i] - c[t])(j le i )$$
    发现之中有前缀和

    $$S_i = sum_{t = 1}^ic[t]$$
    那么化简式子后就成了一个标准的斜率优化
    $$dp[k - 1][j] + S_j = c[i] * j + dp[k][i] - c[i] * i$$

    代码 
    Solution

    题目中,非常关键的一句,**只能运往编号更大的工厂的仓库**。

    这句话直接说明这个就是个最基础的划分 $dp$

    $ dp[i] $ 表示选 $i$ 这个位置建仓库的最小费用,那么状态转移如下。
    $$dp_i = Min(dp_j + sum_{k = j}^{i - 1}p[k]*(sum_x[i] - sum_x[k]) + c[i])$$
    化简得到
    $$sum_p[i] = sum_1^ip[i];dp_i = dp_j + (sum_p[i] - sum_p[j]) * sum_x[i] - sum_{k = j}^{i}p[k] * sum_x[k];
    $$
    发现左边那一部分乘起来的也可用前缀和预先处理,那么便成为了一个标准的斜率优化。
    $$
    dp_i - sum_x[i]*sum_p[i]+sum_x[i] * sum_p[j] - sum_i = dp_j - sum_j
    $$

    代码

    例题
    Solution

    此题描述明显划分型 $dp$,主要是在如何优化到 $O(n)$ 上的。

    设状态为 $dp[i]$ 表示第 $i$ 个人结尾的最大战斗力

    状态转移方程则是:
    $$
    dp[i] = min(dp[j] + (sum[i] - sum[j])^2 * a + b * (sum[i] - sum[j]) + c),(jle i)
    $$
    化简可得
    $$
    dp_i - a * sum[i]^2 + 2a * sum[i] * sum[j] = dp_j+ a * sum[j]^2 + c - sum[j]*b;
    $$
    和其它斜率优化题目不一样的是,斜率 $2a * sum[i]$ 是一个负的值并且要取最大。

    将点点在坐标系上,因为求的是最大值,所以将一条斜率为负的线从最高向下移动,接触到的第一个点也就是最大值。

    由此,单调队列中应该从大到小存放 $y$ 坐标。

    考虑具体如何像普通一样的实现,若斜率大于第一个点的斜率,那么必将取到,反之因为单调性可以直接出队列。

    那么对于队尾位置,组成下凸时绝对取不到。直接弹出
    代码  
    例题  
    Solution

    此题几个任务,拆开后就是模板了。

    一段一段的去柠檬,容易想到划分型。每次取得的 $S_0$ 是区间$(j,i)$的一个众数,容易证明,若当前选择的点是 $i$ 的话,那么被选取得的 $j$ 要满足 $S_j == S_i == S_0$。否则的话,$S_j$ 或 $S_i$被分到其它地方显然是不差于当前决策的。

    那么状态是: $dp[i]$ 表示选择到 $i$ 个位置,并且以 $S_i$ 作为 $S_0$。

    状态转移就是:
    $$
    dp[i] = min(dp[j - 1] + s_i * sum(i,j)^2)
    $$
    个数是 $sum$ 表示的,前面输入时可以预处理出来,$sum(i,j)$ 就成了 $ sum[i] - sum[j] + 1$ 。

    此时,不就是类似于玩具装箱的方程式么。考虑斜率优化,化简式子。
    $$
    dp[i] - s_i * sum[i] ^ 2 + 2s_i(sum[i] + 1)sum[j] = dp_{j-1} + s_j * sum[j] ^ 2;
    $$
    式子显而易见,但不同的是,我需要求 $max$ 但是 $2 * s_i$ 是一个正的值,单调队列无法解决我想要的结果。

    考虑我能求得什么样的点,还是将点放在坐标系上,将一条斜率为 $2 * s_i$ 的线从上往下移动。

    发现在 $x$ 越靠前的点不一定最先碰到,而且对于 $x$ 较大的点,组成的斜率若小于当前这个点的是不可能取到的,斜率单调递增,这些点可以直接删去。对于组成下凸的中间的点,肯定也无法取到。

    考虑到每一个决策点的 $x$ 与 $y$ 单调递增,并且讨论出来的结果都是在后方删点,也并不在前方取点,而是在后方取到合法点,所以这里用单调栈来解决。

    又因为 $S le 10000$ ,开 $vector$ 解决


    代码

  • 相关阅读:
    改变GMF应用程序画布的布局
    Eclipse 3.2下载最多的国家和地区
    让输出的Plugin文件名里包含当前时间
    把SWT包装成Plugin需要修改的地方
    在程序里隐藏但利用Resource Navigator
    GMF应用程序设置背景图片
    给GMF应用程序添加自定义Action
    Graphical Modeling Framework简介
    GMF常见问题
    EReference的containment和container属性
  • 原文地址:https://www.cnblogs.com/jojojojojob/p/12483065.html
Copyright © 2011-2022 走看看