算法-dp斜率优化

前置知识:

凸包


斜率优化很玄学,凭空讲怎么也讲不好,所以放例题。


[APIO2014]序列分割

[APIO2014]序列分割

给你一个长度为 \(n\) 的序列 \(a_1,a_2,...,a_n\)。你可以切 \(k\) 刀,每一刀可以把某一段序列切成两段,然后获得两段和成绩的收益。最后求最大收益和得到最大收益的切割方案。

数据范围:\(2\le n\le 100000,1\le k\le\min\{n-1,200\},1\le a_i\le 10000\)。


首先证明,切的顺序不影响结果。设序列为连着的 \(a,b,c\) 三段。三段的和分别为 \(A,B,C\)。

如果先切开 \(a|b,c\) 再切开 \(a|b|c\),获益为 \(A(B+C)+BC=AB+AC+BC\)。

如果先切开 \(a,b|c\) 再切开 \(a|b|c\),获益为 \((A+B)C+AB=AC+BC+AB\)。

所以以此类推,切割的顺序不影响最终收益大小。


然后开始 \(\texttt{dp}\),\(F_{i,j}\) 表示前 \(i\) 个数切 \(j\) 刀的最大收益,\(s_i=\sum\limits^i_{j=1}a_j\),则有状态转移方程:

\[F_{i,j}=\max\{F_{t,j-1}+s_t(s_i-s_t)\}(0\le t<i)
\]

因为 \(F_{i,j}\) 只由 \(F_{t,j-1}\) 推得,所以可以滚动数组 \(F\),令 \(f_j=F_{i,j}\),\(g_j=F_{i,j-1}\),那么上式就变成:

\[f_i=\max\{g_t+s_t(s_i-s_t)\}(0\le t<i)
\]

如果直接暴力跑一次 \(2\) 重循环的 \(\texttt{dp}\),\(\Theta(n^2k)\) 会 \(\color{#357}{\texttt{TLE}}\),但你仔细观察 \(g_t+s_t(s_i-s_t)\) 这个式子,如果有一个 \(p(0\le p<i)\) 满足

\[g_p+s_p(s_i-s_p)\ge g_t+s_t(s_i-s_t)
\]

则推式可得:

\[(g_p-s_p^2)-(g_t-s_t^2)\ge s_t\cdot s_i-s_p\cdot s_i
\]
\[\therefore\frac{(g_p-s_p^2)-(g_t-s_t^2)}{s_t-s_p}\ge s_i
\]

令 \(slope=\frac{(g_p-s_p^2)-(g_t-s_t^2)}{s_t-s_p}\)

如果把 \((-s_p,g_p-s_p^2)\) 和 \((-s_t,g_t-s_t^2)\) 看作平面直角坐标系中的两个点,那么 \(slope\) 就是这两个点连边的斜率。

因为 \(\texttt{dp}\) 循环 \(i=1\to n\) 时 \(s_i\) 是递增的,而两个点的 \(slope\) 又不是随 \(s_i\) 变化的,所以可以维护一个双头单调队列,每次把 \(i\) 放到队尾,队列满足:

  1. 从左到右数递增。
  2. 从左到右相邻两个数所对应的点连边的斜率递减。

然后维护队列并 \(\texttt{dp}\):

循环 \(j=1\to k\):

赋值滚动数组 \(g=f\),清零 \(f\)。

清空队列并在队列中加入 \(0\)(相当于原点)。

循环 \(i=1\to n\):

把队列头相邻两个数 \(slope\le s_i\) 的踢掉。

取队列头 \(p\),\(f_i=g_p+s_p(s_i-s_p)\)。

因为最终要输出方案,所以记录索引 \(pro_{i,j}=p\)。

把 \(i\) 看作点 \((-s_i,g_i-s_i^2)\),如果队尾相邻元素的 \(slope\ge i\) 和队尾元素的 \(slope\),就把队尾元素踢掉。

队尾加入 \(i\)。

然后在单调队列和斜率优化的加持下,因为维护队列和循环 \(\texttt{dp}\) 的总时间复杂度为 \(\Theta(n)\), 所以总的时间复杂度缩减为 \(\Theta(nk)\)。于是蒟蒻逃脱了 \(\color{#357}{\texttt{TLE}}\) 的风险。


Code:

#include <bits/stdc++.h>
using namespace std;
/*
{a},{b},{c}
a(b+c)+bc=ab+ac+bc\-\Greatitude
(a+b)c+ab=ac+bc+ab/-/
*/
#define lng long long
const int N=1e5+10,K=210;
int n,k,a[N],q[N],p[N][K]; //n,k,ai,queue,方案路径
lng f[N],g[N],sum[N]; //fi,gt,si double funct(int x,int y){ //两个点的slope
if(sum[x]==sum[y]) return -1e16;
return 1.0*((g[x]-sum[x]*sum[x])-(g[y]-sum[y]*sum[y]))/(sum[y]-sum[x]);
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",a+i),sum[i]=sum[i-1]+a[i];
for(int j=1;j<=k;j++){ //维护单调队列+dp
for(int i=1;i<=n;i++) g[i]=f[i],f[i]=0;
int l=1,r=0;
q[++r]=0;
for(int i=1;i<=n;i++){
while(l<r&&funct(q[l],q[l+1])<=sum[i]) l++;
f[i]=g[q[l]]+sum[q[l]]*(sum[i]-sum[q[l]]);
p[i][j]=q[l];
while(l<r&&funct(q[r-1],q[r])>=funct(q[r],i)) r--;
q[++r]=i;
}
}
printf("%lld\n",f[n]); //输出最终最大收益
for(int i=k,j=n;i>=1;i--)
printf("%d%c",j=p[j][i],"\n "[i>1]); //输出切割方案。
return 0;
}

祝大家学习愉快!

dp斜率优化的更多相关文章

  1. 【BZOJ-4518】征途 DP + 斜率优化

    4518: [Sdoi2016]征途 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 230  Solved: 156[Submit][Status][ ...

  2. 【BZOJ-3437】小P的牧场 DP + 斜率优化

    3437: 小P的牧场 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 705  Solved: 404[Submit][Status][Discuss ...

  3. 【BZOJ-1010】玩具装箱toy DP + 斜率优化

    1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 8432  Solved: 3338[Submit][St ...

  4. 【BZOJ】1096: [ZJOI2007]仓库建设(dp+斜率优化)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1096 首先得到dp方程(我竟然自己都每推出了QAQ)$$d[i]=min\{d[j]+cost(j+ ...

  5. BZOJ 1096: [ZJOI2007]仓库建设(DP+斜率优化)

    [ZJOI2007]仓库建设 Description L公司有N个工厂,由高到底分布在一座山上.如图所示,工厂1在山顶,工厂N在山脚.由于这座山处于高原内陆地区(干燥少雨),L公司一般把产品直接堆放在 ...

  6. 学渣乱搞系列之dp斜率优化

    学渣乱搞系列之dp斜率优化 By 狂徒归来 貌似dp的斜率优化一直很难搞啊,尤其是像我这种数学很挫的学渣,压根不懂什么凸包,什么上凸下凸的,哎...说多了都是泪,跟wdd讨论了下,得出一些结论.本文很 ...

  7. DP斜率优化总结

    目录 DP斜率优化总结 任务安排1 任务计划2 任务安排3 百日旅行 DP斜率优化总结 任务安排1 首先引入一道题,先\(O(N^2)\)做法:分别预处理出\(T_i,C_i\)前缀和\(t[i],c ...

  8. HDU 3507 [Print Article]DP斜率优化

    题目大意 给定一个长度为\(n(n \leqslant 500000)\)的数列,将其分割为连续的若干份,使得 $ \sum ((\sum_{i=j}^kC_i) +M) $ 最小.其中\(C_i\) ...

  9. BZOJ 1010: [HNOI2008]玩具装箱toy [DP 斜率优化]

    1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 9812  Solved: 3978[Submit][St ...

随机推荐

  1. mysql语句的书写顺序和执行顺序

    mysql语句的书写顺序和执行顺序有很大差异. 书写顺序,mysql的一般书写顺写为: select <要返回的数据列> from <表名> <join, left jo ...

  2. Elementary OS 使用fcitx安装搜狗词库、搜狗输入法(Linux通用)

    刚开始接触Linux的小伙伴可能比较懵逼,我要使用ibus输入法还是fcitx(小企鹅)输入法,其实这两种都不能说是输入法,Linux中输入法的使用是依赖于输入法框架的,其中搜狗输入法和百度输入法都是 ...

  3. Linux nginx安装篇

    目录 前言 版本 安装 1.下载 2.安装依赖 3.编译安装 4.启动 5.服务启动 参考资料 前言 最近新申请了一台服务器,需要安装下nginx服务,安装nginx不是第一次,之前反反复复也装过,由 ...

  4. MathType如何对齐公式

    作为强大的公式编辑器,MathType为我们的学习.工作带来了极大的便利.比如在写论文时,有了它,就可以轻松就把论文里的公式码完:老师在编写试卷时,利用它,可以快速编写出一份试卷.那么在编写公式时,也 ...

  5. 用FL Studio来给电子音乐混音的方法

    FL Studio也算是音乐人用的比较多的编曲.混音软件了,FL Studio的一大的特色就是电子音乐的制作.尤其是对混音的操作,混音是电音制作过程中一个非常重要的环节,非常重要. 混音是什么?混音的 ...

  6. 通用于wps和excel的ntlm hashes窃取利用方式

    https://evi1cg.me/archives/Get_NTLM_Hashes.html介绍了通过Microsoft Office 窃取 NTLM Hashes. 不过这种插入方法不适用于wps ...

  7. Redis 基础设计结构之三 hash(哈希)

    Redis 有 5 种基础数据结构,分别为:string (字符串).list (列表).set (集合).hash (哈希) 和 zset (有序集合). 今天来说一下hash(哈希),hash的数 ...

  8. java实验作业1

    1 //1已知圆的半径为10,求其周长及面积 2 package calsswork3; 3 4 public class test3_1 { 5 //求周长 6 public static doub ...

  9. CoProcessFunction实战三部曲之一:基本功能

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  10. docker安装myInfluxDB映射本地目录+开机后台自启动

    CentOS7环境 1.docker hup库搜索influxdb docker search influxdb 2.拉取influxdb镜像 docker pull influxdb 3.查看已下载 ...