首页 > 其他分享 >DP斜率优化学习笔记

DP斜率优化学习笔记

时间:2024-08-22 10:29:03浏览次数:7  
标签:return leq int ll 笔记 斜率 tail DP

最后一次修改:2024.7.16 14:39 P.M By 哈哈铭

简介

“斜率优化”顾名思义就是用斜率进行优化,让 \(DP\) 的时间复杂度更优。
一般情况下,将动态转移方程化简后得到这样的关系式:

\[\frac{y_1-y_2}{x_1-x_2} \leq K \]

然后通过该式进行转移,以达到优化时间复杂度的目的。

小tip:推公式前可以先试着打出暴力。

例题(模板题)

P3195 [HNOI2008] 玩具装箱

题目大意

有 \(n\) 个玩具,第 \(i\) 个玩具价值为 \(c_i\)。要求将这 \(n\) 个玩具排成一排,分成若干段。对于一段 \([l, r]\),它的代价为 \((r − l + \sum _{i=l}^{r} c_i − L)^2\)。L 是给定常量,求分段的最小代价。

分析

首先,设 \(s_i\) 为 \(\sum _{j=1}^{i} c_j\),又可以设一个简单易懂的状态:\(f_i\) 表示前 \(i\) 个玩具分段的最小代价,然后可以的到一个暴力的方程: \(f_i=\min_{1 \leq j <i}\left\{f_j+(r-l+s_i-s_j-L)^2\right\}\)。这个是 \(O(n^2)\) 的 \(DP\)。带入另一个 \(k\),思考如何去最优解,考虑让它变形以符合斜率优化的公式。
化简过程省略……推起来太麻烦了
化简可得(当 \(1 \leq k < j \leq n\),同时,\(j\) 比 \(k\) 更优时):

\[\frac{f_j-f_k+(s_j+j)^2-(s_k+k)^2}{s_j+j-s_k-k} \leq 2(s_i+i-L-1) \]

用单调队列进行优化就OK啦~

此处斜率越大越优。

见此图:

很明显,这里的斜率是越大越好(到 \(i\) 的)。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5e4+100;
ll n,L,f[N],s[N],q[N],head,tail;
inline ll mymin(ll x,ll y) {return x<y?x:y;}
inline double P(ll x) {return x*x;}
inline double X(ll x,ll y){return f[x]-f[y]+P(s[x]+x)-P(s[y]+y);}
inline double Y(ll x,ll y){return s[x]+x-s[y]-y;}
int main(){
    scanf("%lld%lld",&n,&L);
    for(int i=1;i<=n;i++){
        scanf("%lld",&s[i]);
        s[i]=s[i-1]+s[i];
    }
    f[0]=0;head=tail=0;
    for(int i=1;i<=n;i++){
        while(head<=tail&&X(q[head+1],q[head])<2*(s[i]+i-L-1)*Y(q[head+1],q[head])) head++;
        f[i]=f[q[head]]+P(i-q[head]+s[i]-s[q[head]]-L-1);
        while(head<=tail&&X(q[tail],q[tail-1])*Y(i,q[tail])>X(i,q[tail])*Y(q[tail],q[tail-1])) tail--;
        q[++tail]=i;
    }
    printf("%lld",f[n]);
    return 0;
}

[APIO2010] 特别行动队

题目大意

与上一题差不多,也是分若干段,但是求的是最大代价,代价的公式也不一样,肯定不一样啊,可是它仍然摆脱不了是一道斜率优化模板题的命运。

分析

这道题很简单。

首先,也是设 \(s_i\) 为 \(\sum _{j=1}^{i} c_j\),然后得到一个 \(O(n^2)\) 的暴力 \(DP\)。

其转移方程为:

\[f_i=\max_{1 \leq j <i}\left\{f_j+a\times (s_i-s_j)^2+b\times (s_i-s_j)+c \right\} \]

带入另一个 k,思考如何去最优解,考虑让它变形以符合斜率优化的公式。

化简过程省略……推起来太麻烦了 化简可得(当 \(1 \leq k < j \leq n\),同时,\(j\) 比 \(k\) 更优时):

\[\frac{(f_j+a\times {s_j}^2)-(f_k+a\times {s_k}^2)}{s_j-s_k} \leq 2 \times a \times s_i+b \]

用单调队列进行优化就OK啦~

此处斜率越小越优。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+100;
ll a,b,c,n,s[N],f[N],q[N],head,tail;
inline ll mymax(ll x,ll y) {return x>y?x:y;}
inline ll P(ll x) {return x*x;}
inline double slope(ll x,ll y){
    return 1.0*((f[y]+a*P(s[y]))-(f[x]+a*P(s[x])))/(s[y]-s[x]);
}
int main(){
//    freopen("t2.in","r",stdin);
//    freopen("t2.out","w",stdout);
    scanf("%lld",&n);
    scanf("%lld%lld%lld",&a,&b,&c);
    for(int i=1;i<=n;i++){
        scanf("%lld",&s[i]);
        s[i]=s[i-1]+s[i];
    }
    f[0]=0;
    for(int i=1;i<=n;i++){
        while(head<tail&&slope(q[head],q[head+1])>=2*a*s[i]+b) head++;
        f[i]=f[q[head]]+a*P(s[i]-s[q[head]])+b*(s[i]-s[q[head]])+c;
        while(head<tail&&slope(q[tail],i)>=slope(q[tail-1],i)) tail--;
        q[++tail]=i;
    }
    printf("%lld",f[n]);
    return 0;
}

征途

题目大意

其实与前两题差不多,只不过是要求了分的段数,公式也是光明正大地给出来了,其实同样简单,也是一道紫题

分析

这道题同样要一个前缀和,设为 \(v_i\) 吧。

然后设出状态:\(f_{i,j}\) 表示遍历到第 \(i\) 条路,走到第 \(j\) 天。

有一个 \(O(n^3)\) 的暴力可以得出。

考虑用斜率优化,因为里面总有奇奇怪怪的计算。

然后,可以得到一个动态转移方程,很简单,就不在此列出了。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=3e3+50;
int n,m;
ll ans,f[N][N],v[N],sum[N],l[N],hd,tl;
double getK(int c,int i,int j) {
    ll x=f[i][c-1]+v[i]*v[i],xx=f[j][c-1]+v[j]*v[j];
    ll y=v[i],yy=v[j];
    return 1.0*(x-xx)/(y-yy);
}
int main(){
//    freopen("journey.in","r",stdin);
//    freopen("journey.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        scanf("%lld",&v[i]);
        v[i]=v[i-1]+v[i];
    }
    for(int i=1;i<=n;++i)
        f[i][1]=v[i]*v[i];
    for(int c=2;c<=m;++c){
        hd=tl=1;
        l[1]=c-1;
        for(int i=c;i<=n;++i){
            while(hd<tl&&getK(c,l[hd],l[hd+1])<2*v[i]) ++hd;
            f[i][c]=f[l[hd]][c-1]+(v[l[hd]]-v[i])*(v[l[hd]]-v[i]);
            while(hd<tl&&getK(c,l[tl],i)<getK(c,l[tl],l[tl-1])) --tl;
            l[++tl]=i;
        }
    }
    printf("%lld",m*f[n][m]-v[n]*v[n]);
    return 0;
}

然后就差不多了。

更多能练手的题目

[NOIP2018 普及组] 摆渡车

[ZJOI2007] 仓库建设

[CEOI2004] 锯木厂选址

$ \Large\mathcal{ Thank\ \ you\ \ very\ \ much!}$

标签:return,leq,int,ll,笔记,斜率,tail,DP
From: https://www.cnblogs.com/huhaoming/p/18373270

相关文章

  • 算法笔记|Day32动态规划V
    算法笔记|Day32动态规划V※※※※※完全背包问题理论基本题目描述题目分析采用一维数组(滚动数组)☆☆☆☆☆leetcode518.零钱兑换II题目分析代码☆☆☆☆☆leetcode377.组合总和Ⅳ题目分析代码☆☆☆☆☆KamaCoder57.爬楼梯(待补充)题目分析代码※※※※※完全......
  • Spark超全笔记 一站式搞定!!
    sparkSparkSpark和Hadoop的区别Spark计算流程Spark组成架构(spark的五大组件)Spark内核调度流程Spark并行度RDDRDD的五大特性RDD的创建RDD常用算子常用transformation算子常用action算子RDD缓存和checkpoint对比RDD依赖依赖管理DAG有向无环图为什么要进行stage划分Spar......
  • 【学习笔记】数学基础:Ferrers 图
    在分拆时我们有的时候很难搞,所以需要引入Ferrers图定义将分拆的每个部分用点组成的行表示,每行点的个数是这个部分的大小根据分拆的定义,Ferrers图中不同的行按照递减的顺序排放分拆:将自然数n写成递降正整数和的表示。\[n=r_1+r_2+\ldots+r_k\quadr_1\ger_2\ge\ldo......
  • BufferQueue的试用笔记
    简介BufferQueue是一个用.NET编写的高性能的缓冲队列实现,支持多线程并发操作。源码网站:https://github.com/eventhorizon-cli/BufferQueue功能设计支持创建多个Topic,每个Topic可以有多种数据类型。每一对Topic和数据类型对应一个独立的缓冲区。支持创建多个Cons......
  • mini-lsm通关笔记Week1Day4
    项目地址:https://github.com/skyzh/mini-lsm个人实现地址:https://gitee.com/cnyuyang/mini-lsmTask1-SSTBuilder在此任务中,您需要修改:src/table/builder.rssrc/table.rsSST由存储在磁盘上的数据块和索引块组成。通常,数据块都是懒加载的-直到用户发出请求,它们才会被加载......
  • 【读书笔记-《30天自制操作系统》-6】Day7
    本篇向着移动鼠标的目标继续前进。先对中断处理进行一些补充说明,然后建立完善缓冲区来实现键盘数据接收。最后是在此基础上的初始化鼠标控制电路与鼠标的数据接收。1.中断处理程序补充说明前面的处理中,接收到键盘中断后只是显示一行信息,现在把按键的信息也一并显示出来......
  • Verilog刷题笔记55
    题目:Exams/ece2412014q5aYouaretodesignaone-inputone-outputserial2’scomplementerMoorestatemachine.Theinput(x)isaseriesofbits(oneperclockcycle)beginningwiththeleast-significantbitofthenumber,andtheoutput(Z)isthe2......
  • 【学习笔记】 陈强-机器学习-Python-Ch11 决策树(Decision Tree)
    系列文章目录监督学习:参数方法【学习笔记】陈强-机器学习-Python-Ch4线性回归【学习笔记】陈强-机器学习-Python-Ch5逻辑回归【课后题练习】陈强-机器学习-Python-Ch5逻辑回归(SAheart.csv)【学习笔记】陈强-机器学习-Python-Ch6多项逻辑回归【学习笔记及课后......
  • 学习笔记---Trie树
    Trie树又叫字典树、前缀树(PrefixTree)、单词查找树或键树,是一种多叉树结构,可以高效存储和查找字符串集合的数据结构.intson[N][26],cnt[N],idx;//0号点既是根节点,又是空节点//son[][]存储树中每个节点的子节点//cnt[]存储以每个节点结尾的单词数量//插入一......
  • Spring笔记(一)
    一、了解Spring1.Spring概述Spring是分层的JavaSE/EE应用full-stack轻量级开源框架,以IOC(InverseOfControl:反转控制)和AOP(AspectOrientedProgramming:面向切面编程)为内核,提供了展现层SpringMVC和持久层SpringJDBC以及业务层事务管理等众多的企业级应用技术,还能整合开......