斜率优化,是一种利用斜率的优化(废话)

关于数论:咕咕咕

部分内容参考自学长

如果有这样的一个状态转移方程:

\[f[i]=\min\limits_{j=L_j}^{R_j}\{f[j]+val(j,i)\}
\]

如果\(val(j,i)=A(i)+B(j)\)可以用单调队列解决

当\(val(j,i)=(A(i)+B(j))^2\)时,就可以考虑斜率优化。

关键词:斜率,凸包,单调队列

特别行动队

看到这题,暴力\(DP\)应该比较好想:

\[f[i]=\max\limits^{i-1}_{j=0}\{f[j]+A\cdot(sum[i]-sum[j])^2+B\cdot(sum[i]-sum[j])+C\}
\]

看见平方,把他展开:

\[f[i]=\max\limits^{i-1}_{j=0}\{f[j]+A\cdot(sum[i]^2+sum[j]^2-2sum[i]\cdot sum[j])+B\cdot(sum[i]-sum[j])+C\}
\]
\[f[i]=\max\limits^{i-1}_{j=0}\{f[j]+A\cdot sum[i]^2+A\cdot sum[j]^2-2A\cdot sum[i]\cdot sum[j]+B\cdot sum[i]-B\cdot sum[j]+C\}
\]

一定的项就可以提出去

\[f[i]=\max\limits^{i-1}_{j=0}\{f[j]+A\cdot sum[j]^2-2A\cdot sum[i]\cdot sum[j]-B\cdot sum[j]\}+A\cdot sum[i]^2+B\cdot sum[i]+C
\]

所以,我们考虑\(val_j=f[j]+A\cdot sum[j]^2-2A\cdot sum[i]\cdot sum[j]-B\cdot sum[j]\)这一部分的取值

设当前想要转移到的状态为\(i\),\(j,k\)是之前的两个转移点

那么当\(j<k\),且\(val_j\leq val_k\)时,\(j\)劣于\(k\)。

写出来就是

\[f[j]+A\cdot sum[j]^2-2A\cdot sum[i]\cdot sum[j]-B\cdot sum[j]\leq f[k]+A\cdot sum[k]^2-2A\cdot sum[i]\cdot sum[k]-B\cdot sum[k]
\]
\[f[j]+A\cdot sum[j]^2-B\cdot sum[j]-f[k]-A\cdot sum[k]^2+B\cdot sum[k]\leq 2A\cdot sum[i]\cdot sum[j]-2A\cdot sum[i]\cdot sum[k]
\]
\[f[j]-f[k]+A\cdot(sum[j]^2-sum[k]^2)+B\cdot(sum[k]-sum[j])\leq 2Asum[i]\cdot(sum[j]-sum[k])
\]
\[\frac{f[j]-f[k]+A\cdot(sum[j]^2-sum[k]^2)+B\cdot(sum[k]-sum[j])}{sum[j]-sum[k]}\geq 2Asum[i]
\]

(注意最后有个变号)

令\(g[t]=f[t]+A\cdot sum[t]^2-B\cdot sum[t]\),原式就会变成:

\[\frac{g[j]-g[k]}{sum[j]-sum[k]}\geq 2Asum[i]
\]

好——舒——服——啊——这就直接上斜率优化了

Latex打得我要死了

看题,发现\(A<0\),而\(sum\)是递增的,所以如果一个\(sum[i]\)满足条件,之后的所有\(sum[i]\)也都满足条件,也就是说对于之后的所有转移,都有\(j\)劣于\(k\)。

冷静分析,发现是个上凸包,因为根据推出来的式子弹队头,斜率大于等于当前的都会被弹掉

然后同样的道理来弹队尾!

(最后一步的分析第一次做斜率优化想了半个小时……不过想过去是真的豁然开朗)

磕磕绊绊弄出来的混乱程序

  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. #define db double
  4. #define ZZ_zuozhe int main()
  5. #define Arknights return 0
  6. using namespace std;
  7. #define MAXN 1000005
  8. ll q[MAXN];
  9. db f[MAXN],k[MAXN],sum[MAXN],y[MAXN];
  10. ZZ_zuozhe
  11. {
  12. ll n;
  13. scanf("%lld",&n);
  14. db a,b,c,nowk;
  15. scanf("%lf%lf%lf",&a,&b,&c);
  16. for(int i=1;i<=n;i++)
  17. {
  18. scanf("%lf",&sum[i]);
  19. sum[i]+=sum[i-1];
  20. }
  21. ll h=1,t=1,j;
  22. for(int i=1;i<=n;i++)
  23. {
  24. nowk=2*a*sum[i];
  25. while(h<t&&k[h]>=nowk)h++;
  26. f[i]=y[q[h]]-nowk*sum[q[h]]+(a*sum[i]+b)*sum[i]+c;
  27. y[i]=f[i]+(a*sum[i]-b)*sum[i];
  28. while(h<t&&k[t-1]<=(y[q[t]]-y[i])/(sum[q[t]]-sum[i]))--t;
  29. k[t]=(y[q[t]]-y[i])/(sum[q[t]]-sum[i]);
  30. q[++t]=i;
  31. }
  32. printf("%.0lf\n",f[n]);
  33. Arknights;
  34. }

我的娘啊太短了吧……其实还可以再压行

有一点需要注意的是因为单调队列里存了线段斜率,这是代表两个点的,所以这里只能用\(f<t\)配\(f=t=1\),我原来写的单调队列这里会挂……

玩具装箱

先打暴力:

\[f[i]=\min\limits_{j=1}^{i-1}\{f[j]+(sum[i]-sum[j]+i-j-1-L)^2\}
\]

按照我们的一贯作风,这个时候应该化简了

这一大坨平方摆在这里化个麦乐鸡腿堡啊!

不急不急,咱们换个元,把含\(i,j\)的项合在一起,令\(g[t]=sum[t]+t\),这里就有

\[f[i]=\min\limits_{j=1}^{i-1}\{f[j]+(g[i]-g[j]-1-L)^2\}
\]

然后因为\(g[i]\)是可求的,直接把他提出来就好

\[f[i]=\min\limits_{j=1}^{i-1}\{f[j]+g[i]^2+(g[j]+1+L)^2-2\cdot g[i]\cdot (g[j]+1+L)\}
\]
\[f[i]=\min\limits_{j=1}^{i-1}\{f[j]+(g[j]+1+L)^2-2\cdot g[i]\cdot (g[j]+1+L)\}+g[i]^2
\]

接下来,考虑\(f[j]+(g[j]+1+L)^2-2\cdot g[i]\cdot (g[j]+1+L)\)这一部分

设当前要转移到\(i\),之前有两个状态\(j,k\)

则当\(j<k\),且\(val_j \geq val_k\)时,\(j\)劣于\(k\)。

列出式子:

\[f[j]+(g[j]+1+L)^2-2\cdot g[i]\cdot (g[j]+1+L)\geq f[k]+(g[k]+1+L)^2-2\cdot g[i]\cdot (g[k]+1+L)
\]

然后就是欢乐化简环节,这里就直接给结果了\(qwq\)

\[\frac{f[j]-f[k]+(g[j]+L+1)^2-(g[k]+L+1)^2}{g[j]-g[k]}\leq 2\cdot g[i]
\]

(注意变号)

令\(h[t]=f[t]+(g[t]+L+1)^2\),则有

\[\frac{h[j]-h[k]}{g[j]-g[k]}\leq 2\cdot g[i]
\]

一般形式出来了,代码就好写了~

右面单调递增,和上一题恰恰相反,单调队列下凸包,代码仍然极短>.<

  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. #define db double
  4. #define ZZ_zuozhe int main()
  5. #define Arknights return 0
  6. using namespace std;
  7. #define MAXN 50005
  8. ll q[MAXN];
  9. db f[MAXN],g[MAXN],h[MAXN],sum[MAXN],L,k[MAXN];
  10. db nowk;
  11. ll n;
  12. inline db slope(ll j,ll k){return (h[j]-h[k])/(g[j]-g[k]);}
  13. ZZ_zuozhe
  14. {
  15. scanf("%lld%lf",&n,&L);
  16. h[0]=(L+1)*(L+1);
  17. for(int i=1;i<=n;i++)
  18. {
  19. scanf("%lf",&sum[i]);
  20. sum[i]+=sum[i-1];
  21. g[i]=sum[i]+i;
  22. }
  23. ll he=1,t=1;
  24. for(int i=1;i<=n;i++)
  25. {
  26. nowk=2*g[i];
  27. while(he<t&&k[he]<=nowk)he++;
  28. f[i]=f[q[he]]+(g[i]-g[q[he]]-1-L)*(g[i]-g[q[he]]-1-L);
  29. h[i]=f[i]+(g[i]+L+1)*(g[i]+L+1);
  30. while(he<t&&k[t-1]>=slope(q[t],i))t--;
  31. k[t]=slope(q[t],i);
  32. q[++t]=i;
  33. }
  34. printf("%.0lf",f[n]);
  35. Arknights;
  36. }

仓库建设

上暴力!

\[f[i]=\min\limits_{j=1}^{i-1}\{f[j]+C[i]+(\sum\limits_{k=j+1}^{i}P[k]\cdot(X[i]-X[k]))\}
\]

还要求和好麻烦啊……那就化简一下

\[f[i]=\min\limits_{j=1}^{i-1}\{f[j]+C[i]+\sum\limits_{k=j+1}^{i}P[k]\cdot X[i]-\sum\limits_{k=j+1}^{i}P[k]\cdot X[k]\}
\]

这个求和就可以前缀和优化了呢……

设\(sP[t]=\sum\limits_{i=1}^{t}P[i]\),\(sPX[t]=\sum\limits_{i=1}^{t}P[i]\cdot X[i]\),就可以得到:

\[f[i]=\min\limits_{j=1}^{i-1}\{f[j]+C[i]+X[i]\cdot (sP[i]-sP[j])-(sPX[i]-sPX[j])\}
\]

怎么越来越乱了,走了走了打暴力去

且慢!你似乎忘了把只含\(i\)的式子提出来……

于是:

\[f[i]=\min\limits_{j=1}^{i-1}\{f[j]-X[i]\cdot sP[j]+sPX[j]\}+C[i]+X[i]\cdot sP[i]-sPX[i]
\]

咱就只用考虑\(f[j]-X[i]\cdot sP[j]+sPX[j]\)这一块

设当前要转移到\(i\),之前有两个状态\(j,k\)(套路出来了

则当\(j<k\),且\(val_j \geq val_k\)时,\(j\)劣于\(k\)。

代入就是

\[f[j]-X[i]\cdot sP[j]+sPX[j]\geq f[k]-X[i]\cdot sP[k]+sPX[k]
\]
\[f[j]-f[k]+sPX[j]-sPX[k]\geq X[i]\cdot (sP[j]-sP[k])
\]
\[\frac{f[j]-f[k]+sPX[j]-sPX[k]}{sP[j]-sP[k]}\leq X[i]
\]

令\(g[t]=f[t]+sPX[t]\),强行搞出一般形式:

\[\frac{g[j]-g[k]}{sP[j]-sP[k]}\leq X[i]
\]

舒服了,上兵器,和上一题类似,单调队列下凸包。

这题数据很大时\(double\)似乎精度堪忧……换了\(long long\)就没事了,不过记得把所有不等号的等于都去掉……

  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. #define db double
  4. #define ZZ_zuozhe int main()
  5. #define Arknights return 0
  6. using namespace std;
  7. #define MAXN 1000005
  8. ll n;
  9. ll q[MAXN];
  10. ll p[MAXN],x[MAXN],c[MAXN],sp[MAXN],spx[MAXN],f[MAXN],nowk,k[MAXN],g[MAXN];
  11. inline db slope(ll j,ll k){return 1.0*(g[j]-g[k])/(sp[j]-sp[k]);}
  12. ZZ_zuozhe
  13. {
  14. scanf("%lld",&n);
  15. for(int i=1;i<=n;i++)
  16. {
  17. scanf("%lld%lld%lld",&x[i],&p[i],&c[i]);
  18. sp[i]=sp[i-1]+p[i];
  19. spx[i]=spx[i-1]+p[i]*x[i];
  20. }
  21. ll h=1,t=1;
  22. for(int i=1;i<=n;i++)
  23. {
  24. nowk=x[i];
  25. while(h<t&&k[h]<nowk)h++;
  26. f[i]=f[q[h]]-x[i]*sp[q[h]]+spx[q[h]]+c[i]+x[i]*sp[i]-spx[i];
  27. g[i]=f[i]+spx[i];
  28. while(h<t&&k[t-1]>slope(q[t],i))t--;
  29. k[t]=slope(q[t],i);
  30. q[++t]=i;
  31. }
  32. printf("%lld",f[n]);
  33. Arknights;
  34. }

土地购买

发现每次买地是按最大长和最大宽的乘积计费的,所以如果某块土地的长和宽都小于另外某块土地的,这块土地实际上就不会对答案产生贡献了

然后就可以写dp:(记得考虑已知性质

\[f[i]=\min\limits_{j=1}^{i-1}\{f[j]+w[j+1]*l[i]\}
\]

通过排序使\(l\)单调递增,\(w\)单调递减,就可以上斜率优化了

设当前要转移到\(i\),之前有两个状态\(j,k\)(我又开始复读了

则当\(j<k\),且\(val_j \geq val_k\)时,\(j\)劣于\(k\)。

代入:

\[f[j]+w[j+1]*l[i]\geq f[k]+w[k+1]*l[i]
\]
\[\frac{f[j]-f[k]}{w[k+1]-w[j+1]}\leq l[i]
\]

莫名很好化……

右面单调递增,单调队列维护个下凸包即可

讲个笑话,我输出\(f[n]\),\(Wa\) \(10\)调了半上午:)

  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. #define db double
  4. #define ZZ_zuozhe int main()
  5. #define Arknights return 0
  6. #define MAXN 50005
  7. using namespace std;
  8. struct land
  9. {
  10. ll w,l;
  11. }a[MAXN],b[MAXN];
  12. ll cnt=0;
  13. inline bool cmp(land a,land b){return (a.l!=b.l?a.l<b.l:a.w<b.w);}
  14. ll n,q[MAXN];
  15. db f[MAXN],k[MAXN];
  16. inline db slope(ll j,ll k){return 1.0*(f[j]-f[k])/(b[k+1].w-b[j+1].w);}
  17. ZZ_zuozhe
  18. {
  19. scanf("%lld",&n);
  20. for(int i=1;i<=n;i++)scanf("%lld%lld",&a[i].w,&a[i].l);
  21. sort(a+1,a+n+1,cmp);
  22. for(int i=1;i<=n;i++)
  23. {
  24. while(cnt&&(a[i].w>=b[cnt].w||a[i].l==b[cnt].l))cnt--;
  25. b[++cnt].w=a[i].w;
  26. b[cnt].l=a[i].l;
  27. }
  28. //for(int i=1;i<=cnt;i++)cout<<b[i].l<<' '<<b[i].w<<endl;
  29. ll h=1,t=1;
  30. db nowk;
  31. for(int i=1;i<=cnt;i++)
  32. {
  33. nowk=b[i].l;
  34. //cout<<"i:"<<i<<' '<<k[h]<<' '<<nowk<<endl;
  35. while(h<t&&k[h]<nowk)h++;
  36. f[i]=f[q[h]]+b[q[h]+1].w*b[i].l;
  37. while(h<t&&k[t-1]>slope(q[t],i))t--;
  38. k[t]=slope(q[t],i);
  39. q[++t]=i;
  40. //cout<<"head:"<<h<<' '<<"f["<<i<<"]="<<f[i]<<endl;
  41. }
  42. printf("%.0lf",f[cnt]);
  43. Arknights;
  44. }

【刷题笔记】DP优化-斜率优化的更多相关文章

  1. LeetCode刷题笔记-DP算法-取数问题

    题目描述 (除数博弈论)爱丽丝和鲍勃一起玩游戏,他们轮流行动.爱丽丝先手开局. 最初,黑板上有一个数字 N .在每个玩家的回合,玩家需要执行以下操作: 选出任一 x,满足 0 < x < ...

  2. 【学习笔记】动态规划—斜率优化DP(超详细)

    [学习笔记]动态规划-斜率优化DP(超详细) [前言] 第一次写这么长的文章. 写完后感觉对斜优的理解又加深了一些. 斜优通常与决策单调性同时出现.可以说决策单调性是斜率优化的前提. 斜率优化 \(D ...

  3. dp的斜率优化

    对于刷题量我觉得肯定是刷的越多越好(当然这是对时间有很多的人来说. 但是在我看来我的确适合刷题较多的那一类人,应为我对知识的应用能力并不强.这两天学习的内容是dp的斜率优化.当然我是不太会的. 这个博 ...

  4. 《Data Structures and Algorithm Analysis in C》学习与刷题笔记

    <Data Structures and Algorithm Analysis in C>学习与刷题笔记 为什么要学习DSAAC? 某个月黑风高的夜晚,下班的我走在黯淡无光.冷清无人的冲之 ...

  5. PTA刷题笔记

    PTA刷题记录 仓库地址: https://github.com/Haorical/Code/tree/master/PTA/GPLT 两周之内刷完GPLT L2和L3的题,持续更新,包括AK代码,坑 ...

  6. Python 刷题笔记

    Python 刷题笔记 本文记录了我在使用python刷题的时候遇到的知识点. 目录 Python 刷题笔记 选择.填空题 基本输入输出 sys.stdin 与input 运行脚本时传入参数 Pyth ...

  7. 【笔记篇】斜率优化dp(四) ZJOI2007仓库建设

    传送门戳这里>>> \(n\leq1e6\), 显然还是\(O(n)\)的做法. 这个题有个条件是只能运往编号更大的工厂的仓库, 这也是写出朴素dp的方程的条件. 我们令\(f[i] ...

  8. 【笔记篇】斜率优化dp(一) HNOI2008玩具装箱

    斜率优化dp 本来想直接肝这玩意的结果还是被忽悠着做了两道数论 现在整天浑浑噩噩无心学习甚至都不是太想颓废是不是药丸的表现 各位要知道我就是故意要打删除线并不是因为排版错乱 反正就是一个del标签嘛并 ...

  9. 【笔记篇】斜率优化dp(二) SDOI2016征途

    =======传=送=门======= 搜题目名会搜出很多奇怪的东西... 这个题目似乎有点毒? 比如在bzoj和loj上可以1A的代码上会在luogu TLE 2个点, 在cogs TLE 10个点 ...

随机推荐

  1. [Luogu P3626] [APIO2009] 会议中心

    题面 传送门:https://www.luogu.org/problemnew/show/P3626 Solution 如果题目只要求求出第一问,那这题显然就是大水题. 但是加上第二问的话...... ...

  2. Servlet学习笔记(三)

    目录 Servlet学习笔记(三) 一.HTTP协议 1.请求:客户端发送欸服务器端的数据 2.响应:服务器端发送给客户端的数据 3.响应状态码 二.Response对象 1.Response设置响应 ...

  3. 第4章 Function语意学

    第4章 Function语意学 目录 第4章 Function语意学 4.1 Member的各种调用方式 Nonstatic Member Function(非静态成员函数) virtual Memb ...

  4. 通过Portwigge的Web安全漏洞训练平台,学习SSRF

    前言 Portswigger是Burpsuite的官网,也是一个非常好的漏洞训练平台.其Web安全靶场地址为:https://portswigger.net/web-security/ 该靶场的训练内 ...

  5. 内网渗透 day4-meterpreter基本命令

    meterpreter基本命令 目录 1.getuid 查看当前用户 1 2.getpid 查看当前的进程id 1 3.getsystem 初步提权 1 4.ps 1.查看进程列表2.帮助我们获取pi ...

  6. 如何让Visual Studio 2019更好用(VS2019配置指南)

    今天电脑没带,借用外面的电脑配环境来用.刚下载完的VS是这样的: UI挺好看的,但代码窗口看起来就和上个世纪的VC6没什么区别,快捷键用起来也不顺手.(2333) 接下来,我们将一步步优化编写环境,让 ...

  7. JS中的Array之方法(2)

    colors=['red','green','black','blue']; (1).  concat(element[|other array])  //联接数组 colors.concat('ye ...

  8. 【JVM第一篇--类加载机制】类加载过程

    写在前面的话:本文是在观看尚硅谷JVM教程后,整理的学习笔记.其观看地址如下:尚硅谷2020最新版宋红康JVM教程 一.什么是类加载过程 (1).概述 我们编写的类(.java文件)会被编译器(如ja ...

  9. 数据库Sharding的基本思想和切分策略(转)

    一.基本思想 Sharding的基本思想就要把一个数据库切分成多个部分放到不同的数据库(server)上,从而缓解单一数据库的性能问题.不太严格的讲,对于海量数据的数据库,如果是因为表多而数据多,这时 ...

  10. 用seaborn绘制散点图

    散点图可以显示观察数据的分布,描述数据的相关性,matlibplot也可以绘制散点图,不过我一般优先使用seaborn库的sctterplot()绘制,下面就介绍一下如何用seaborn.scatte ...