学习笔记·斜率优化 [HNOI2008]玩具装箱
\(qwq\)今天\(rqy\)给窝萌这些蒟蒻讲了斜率优化……大概是他掉打窝萌掉打累了吧顺便偷了\(rqy\)讲课用的图
\(Step \ \ 1\) 一点小转化
事实上斜率优化是专门用来处理这样一类\(dp\)式子的 $$dp_i = A_i + \max \limits _{j = 1}^{i -1}(B_j - C_j \times base_i)$$窝萌尝试把上式中的\(B_j\)、\(C_j\)和\(base_i\)等价成\(x_j\)、\(y_j\)和\(k_i\),并把它们丢到一个平面上,然后它萌就会变成一堆点\((x_j,y_j)\),画一条过他们的直线,类似于$$y - y_j = k_i(x - x_j)$$变换一下$$y = k_ix + (y_j + x_j)$$窝萌会发现,现在窝萌所求的不过是一条截距最大的直线而已。那么其实就是相当于求一个给定\(k\)意义下最靠上的点。
\(qwq\)那么窝萌不妨先减弱问题的不可做性,使其单调——令\(x\)单调增、\(k_i\)单调减。
那当窝萌在朴素\(DP\)遍历所有的\(k_i\)时,所得到的直线的轨迹应该是这样的:
(上图是个\(GIF\)……不动的话就拖出来看吧)轨迹正好是一个凸壳,并且你会发现它的轨迹正好是再绕着每个斜率下最优的点旋转。
\(rqy\)对此是这么解释的:
可以发现好多点是没有机会作为最优的点的。
形象⼀点的说,如果⼀个点的左边和右边某两个点在它上⽅“搭起”了⼀条线段,那么它就永远不会被选到。
而因为我们保证了\(x\)和\(k\)的单调性,所以就我们可以比较方便的考虑“如何选取当前最优点”这个问题。我们考虑遇到一个新的点,是否把他加入最优解集合里面,其实质就是维护一个上凸壳。那么已知两个点\(A\)和\(B\),现在遇到了新加入的点\(C\),此时有两种情况:
1、\(BC\)的斜率大于\(AB\)的斜率
这时我们需要抛弃\(B\),直接连接\(AC\):
2、\(BC\)的斜率小于\(AB\)的斜率
通过这种方式我们就可以完成对整个凸壳的维护。而在判定时也很简单,用叉积来判断即可,即有\(A\)、\(B\)、\(C\)三个点分别是\((x_A,y_A),(x_B,y_B),(x_C,y_C)\)满足\(x_A \leq x_B \leq x_C\),那么如果$$(x_C-x_A)(y_B - y_A) - (x_B - x_A)(y_C- y_A) < 0$$则证明是第一种情况,否则是第二种。
\(emmmm\)直接求斜率当然也是可以的吧,不过会不会很慢很麻烦啊\(qwq\)。
\(Step \ \ 2\) 代码实现以及拓展
\(emmm\)我们考虑用一个逻辑上单调的队列来实现去掉不优的状态 +加入新的状态。回归上题,我们所求的是\(max\),所以我们需要去掉那些斜率小的状态;同时需要加入斜率大的状态。由于整个过程牵扯到前后删点,所以用双端队列来维护。本蒟蒻的(伪)代码如下:
int queue[MAXN] ;
int head = 0, tail = 0 ;
for(i = 1; i <= N; i ++){
while (1){
A = queue[head], B = queue[head + 1] ;
if (y[A] - k[i] * x[A] < y[B] - k[i] * x[B]) head ++ ;
else break ;
}
dp[i] = base[i] + y[A] - k[i] * x[A] ;
Maybe y[i] needs calcing ?
Maybe x[i] also needs calcint ?
So, Calc_it() ;
while (tail - head >= 2){
A = queue[tail - 1], B = queue[tail] ;
if ((y[i] - y[A]) * (x[B] - x[A])
- (x[i] - x[A]) * (y[B] - y[A]) > 0)
tail-- ;
else break ;
}
queue[++ tail] = i ;
}
对了……我在啃这个地方时出了个\(bug\)……那是因为我一直以为\(y\)和\(x\)从本质上来讲有\(n^2\)个……\(OTZ\)
那么对于拓展而言,我们以上做的一切都是基于“\(x\)和\(k\)单调”这一前提的,那么还会有以下两种情况:
1、\(x\)不单调
那么实质上就是我们需要动态插入删除、从内部删除,那么就需要用平衡树来维护一个凸包了 。
2、\(k\)不单调
那么实质上就是我们原来比较方便的第一个\(while\)——从左往右直接删是不行的了,因为现在不优不代表之后不优,所以此时我们需要的就是三分一个位置而不是从前向后扫、
哦,还有,对于\(dp\)式子而言,如果它长这个样子:$$dp_i = A_i + \min \limits _{j = 1}^{i -1}(B_j - C_j \times base_i)$$那么其实我们维护的就是一个下凸壳,所以此时只需要把所有的大于号改成小于号即可。
嗝……那么下面就来看题吧
#\(\mathcal{\color{red}{Description}}\)
\(Link\)
给定一个序列\(C\)和一个常数\(P\) 。你要将序列划分成若干段;每一段如果从\(L\)到\(R\)、整段的和为\(S\) ,则会产生\((R-L+S-P)^2\)的代价,求最小代价。
#\(\mathcal{\color{red}{Solution}}\)
对于这个题而言,我们考虑先把这个恶心的式子化简一下:$$(R-L+S-P)^2 = (R -L + \sum \limits_{i=L}^{R}{C_i} +P)^2 = (\sum \limits_{i=L}^{R}{C_i + 1} - (P + 1)) ^ 2$$然后我们发现这玩意儿只要我们一开始把输入的所有\(C_i\)和\(P++\)就好了……但我并没有这么做\(qwq\)……因为这是\(rqy\)的做法\(hhhhh\)。
回归刚才的,我们观察原式其实可以得出不同的式子,比如我们把\((\sum \limits _{i=L}^{R}{C_i + 1} - (P + 1))^2\)写成 \((S - L) ^ 2\),那么就是$$(sum_R - sum{L-1} + (R - L - 1)-P-1)^2$$而对于比较朴素的\(dp\)方程来讲,他大概长这样$$dp_i = min(dp_j + (sum_R - sum_{L} + (R - L)-P-1)^2)$$窝萌不妨记一个\(A_i = sum_i + i,B_i = sum_i+i+P+1\),那么原来的方程就可以写作$$dp_i = min(dp_j + (A_i-B_j)^2)$$稍微展开一下就是$$dp_i = dp_j+A_i^2 + B_j^2 - 2A_iB_j$$然后你就会发现跟斜率优化的板子们惊人的相似……其中\(A_i\) 只跟\(i\) 有关,看作模型中的\(A_i\) (常项),\(dp_j+B_j^2\) 看作模型中的\(B_j\) ,\(B_j\)看作模型中的\(C_j\) ,\(2A_i\) 看作模型中的\(base_i\),即斜率\(k\),跑一个斜率优化\(DP\)即可。
#include <cstdio>
#include <iostream>
#define MAXN 200010
using namespace std ; int T1, T2 ;
int N, L, head = 1, tail = 1, que[MAXN], i ;
double S[MAXN], A[MAXN], B[MAXN], dp[MAXN] ;
inline double qr(){
int k = 0 ; char c = getchar() ;
while (c < '0' || c > '9') c = getchar() ;
while (c <= '9' && c >= '0') k = (k << 1) + (k << 3) + c - 48, c = getchar() ;
return (double)k ;
}
inline double get_x(int now){return B[now] ;}
inline double get_y(int now){return dp[now] + B[now] * B[now] ;}
inline double get_K (double a, double b){
return ( get_y(a) - get_y(b) )/ ( get_x(a) - get_x(b) ) ;
}
int main(){
cin >> N >> L ;
for (i = 1; i <= N; i ++) S[i] = qr() + S[i - 1] ;
for (i = 0; i <= N; i ++) A[i] = S[i] + i, B[i] = A[i] + L + 1 ;
for (i = 1; i <= N; i ++){
while (head < tail && get_K(que[head], que[head + 1]) < 2 * A[i]) ++ head ;
dp[i] = dp[que[head]] + (A[i] - B[que[head]]) * (A[i] - B[que[head]]) ;
while (head < tail && get_K(que[tail - 1], i) < get_K(que[tail], que[tail - 1])) -- tail ;
que[++ tail] = i ;
}
cout << ((long long)dp[N]);
}
```
学习笔记·斜率优化 [HNOI2008]玩具装箱的更多相关文章
- 斜率优化dp学习笔记 洛谷P3915[HNOI2008]玩具装箱toy
本文为原创??? 作者写这篇文章的时候刚刚初一毕业…… 如有错误请各位大佬指正 从例题入手 洛谷P3915[HNOI2008]玩具装箱toy Step0:读题 Q:暴力? 如果您学习过dp 不难推出d ...
- BZOJ 1010: [HNOI2008]玩具装箱toy [DP 斜率优化]
1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 9812 Solved: 3978[Submit][St ...
- BZOJ 1010: [HNOI2008]玩具装箱toy 斜率优化DP
1010: [HNOI2008]玩具装箱toy Description P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再 ...
- 【BZOJ 1010】 [HNOI2008]玩具装箱toy (斜率优化)
1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 9330 Solved: 3739 Descriptio ...
- bzoj 1010 [HNOI2008]玩具装箱toy(DP的斜率优化)
1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 7874 Solved: 3047[Submit][St ...
- bzoj1010[HNOI2008]玩具装箱toy 斜率优化dp
1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 11893 Solved: 5061[Submit][S ...
- [HNOI2008]玩具装箱TOY --- DP + 斜率优化 / 决策单调性
[HNOI2008]玩具装箱TOY 题目描述: P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京. 他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器 ...
- BZOJ 1010: [HNOI2008]玩具装箱toy(DP+斜率优化)
[HNOI2008]玩具装箱toy Description P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊 ...
- bzoj1010: [HNOI2008]玩具装箱toy(DP+斜率优化)
1010: [HNOI2008]玩具装箱toy 题目:传送门 题解: 很明显的一题动态规划... f[i]表示1~i的最小花费 那么方程也是显而易见的:f[i]=min(f[j]+(sum[i]-su ...
随机推荐
- python中作用域
Python 中,一个变量的作用域总是由在代码中被赋值的地方所决定的. 函数定义了本地作用域,而模块定义的是全局作用域.如果想要在函数内定义全局作用域,需要加上global修饰符. 变量名解析:LEG ...
- ctcms Nginx 伪静态
location /whole { rewrite ^/whole/(.+).html$ /index.php?c=whole&key=$1; } location /show { rewri ...
- elixir 表单 map
键-值对 iex(13)> map = %{:a => 1, 2 => :b}%{2 => :b, :a => 1}iex(14)> map[:a]1iex(15) ...
- 一文详解 Linux 系统常用监控工具(top,htop,iotop,iftop)
概 述 本文主要记录一下 Linux 系统上一些常用的系统监控工具,非常好用.正所谓磨刀不误砍柴工,花点时间总结一下是值得的! 本文内容脑图如下: top 命令 top 命令我想大家都挺熟悉吧! ...
- 润乾报表JSF FORM 标签中使用填报表解决方案
需求 润乾报表的标签需要在jsf页面中和其他填报控件一起使用,润乾报表负责展现录入部分数据,并且这部分数据和页面的其它控件的数据存在前台和后台的交互. 问题 润乾报表在前台会生成自己的form, ...
- linux rabbitmq 安装
下载 在安装 erlang 时使用的是源码包21.0版本:接着下载 rabbitmq-server/3.7.7 的源码包,编译时报错,说 erlang 版本号不满足条件,erlang版本>=19 ...
- 风险管理,未雨绸缪——《代码之殇》读书笔记II
这次的内容主要是关于软件开发过程中的风险管理,包括项目用时估计.产品的发布与更新.承诺兑现的重要性. ①项目用时估计 有人会质疑项目用时估计的可靠性,因为就事而言,这次的任务可能和上次不一样了,开发环 ...
- CSS3 响应式web设计,CSS3 Media Queries
两种方式,一种是直接在link中判断设备的尺寸,然后引用不同的css文件: <link rel="stylesheet" type="text/css" ...
- 最常见到的runtime exception 异常
ArithmeticException, 算术异常ArrayStoreException, 将数组类型不兼容的值赋值给数组元素时抛出的异常BufferOverflowException, 缓冲区溢出异 ...
- Windows2003系统取消关机提示的方法
方法有两种:1.编辑组策略 打开“开始”-“运行”,在“打开”一栏中输入“gpedit.msc”命令打开组策略编辑器,依次展开“计算机配置”→“管理模板”→“系统”,双击右侧窗口出现的“显示‘关闭事件 ...