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

斜率优化学习笔记

时间:2023-01-12 15:24:29浏览次数:59  
标签:le int sum 笔记 times 斜率 优化 dp

前置芝士:

  1. 一次函数(好吧其实你只要知道斜率)
  2. 基本的动态规划能力(暴力的转移)
  3. 一定的数学能力(指拆平方括号和合并同类项

Part.0 目录

  1. 铺垫
  2. 基本策略
  3. 什么题适合&需要斜率优化
  4. 基础习题讲解
  5. 拓展习题讲解

Part 1.铺垫

什么是斜率?

对函数有了解的人都知道,在一次函数中,对于平面直角坐标系的任意两点 \((x_1,y_1)\) 和 \((x_2,y_2)\),连接两点的直线的斜率 \(k=\frac{y_1-y_2}{x_1-x_2}\),这个想必大佬们早已知晓。

Part.2 基本策略

斜率如何帮助我们对动态规划进行优化呢?

首先,对于一个任意裸的状态转移方程,一般来说我们需要循环枚举所有可能的决策点,然后维护答案,但是在一些情况下这么做太慢的,容易惨遭 \(TLE\) ,那么我们应该怎么办?

HDU-3507 为例,状态转移方程不再赘述,假设 \(i\) 有两个决策点 \(j\) 和 \(k\),且 \(j \le k\),那么 \(j\) 成为更优决策点的条件是:

\(dp_j+(sum_i-sum_j)^2+m \le dp_k+(sum_i-sum_k)^2+m\)

\(\Leftrightarrow sum_i^2+dp_j-2\times sum_i \times sum_j +sum_j^2\le sum_i^2+dp_k-2\times sum_i \times sum_k +sum_k^2\)

\(\Leftrightarrow dp_j+sum_j^2 - 2 \times sum_i \times sum_j \le dp_k+sum_k^2 - 2 \times sum_i \times sum_k\)

\(\Leftrightarrow (dp_j+sum_j^2)-(dp_k+sum_k^2) \le 2 \times sum_i \times (sum_j-sum_k)\)

\(\Leftrightarrow \frac {(dp_j+sum_j^2)-(dp_k+sum_k^2)}{sum_j-sum_k} \le 2 \times sum_i\)

这个形式有没有想到什么?

这不就是斜率的标准形式吗?

那么,可以将目前可行的决策点丢进一个队列里,然后只要从队首开始,通过斜率判断此时的队头和队列第二项作为决策点是的斜率不等式是否成立,只要成立,那么 \(j\) 永远不会比 \(k\) 优,就 pop 掉,然后继续向上找的符合条件的决策点,反之,直接状态转移方程。

值得一提的是,很多 blog 会画图用上凸壳下凸壳解释,这样确实便于理解但是我这里为了思路简单明了(说白了就是不可能每道题去画图吧)直接从理论分析了 qwq

代码语言则为:while(l+1<=r&&top(q[l+1],q[l])<=2*sum[i]*down(q[l+1],q[l]))l++;

top & down 分别指分子公式 & 分母公式。

因为要提取的不止队首,所以这个队列要手写(不过你 front 两次也没问题但是又不好写常数又大)。

然后,为了答案的最优性,队列中每相邻两点的对应斜率单调递增,不然反正之后也会被之前的步骤拜拜掉,所以,该队列实际上是一个单调队列

这样处理之后,就可以排除很多的臃余状态,时间复杂度由 \(O(n^2) -> O(n)\)

即:while(l+1<=r&&top(i,q[r])*down(q[r],q[r-1])<=top(q[r],q[r-1])*down(i,q[r]))r--;

然后将当前节点入队即可。

还有一个细节,为了 while 能顺利执行,我们要在动态规划之前加入一个虚拟节点即 q[++r]=0;

那么到这里,这一题基本结束,斜率优化的基本步骤也到此为止。

Part.3 什么题目应当使用斜率优化呢?

显然,为了能构成斜率,状态转移方程形式如下:

\(dp_i=min(dp_j+A(i,j));\)

其中,\(A(i,j)\) 表示与 \(i,j\) 有关的多项式。

如果后面是仅与 \(i\) 或 \(j\) 有关的多项式就考虑单调队列优化罢。

Part.4 基础习题

1.HDU-3507
见上。

2.P3195 [HNOI2008]玩具装箱

先推柿子。

基本状态转移方程:

\(dp_i=\min(dp_j+(i-j-1+sum_i-sum_j-L)^2);\)

仍然假设 \(i\) 有两个决策点 \(j\) 和 \(k\),且 \(j \le k\)。

接下来怎么办呢?爆展?\(72\) 项得有多大的耐力啊,肝帝罢。

可以按照与 \(i\) 相关和与 \(j\) 相关归纳一下:\(dp_i=min(dp_j+((i+sum_i-1-L)-(j+sum_j))^2);\)

这个东西是可以预处理出来的呀!!1

设 \(h_i=i+sum_i-1-L\),\(g_i=j+sum_j\)

接下来就清爽多了.

参考代码:

#include<bits/stdc++.h>
#define int long long//不开ll见祖宗
using namespace std;
int n,m,q[50005],sum[50005],dp[50005],h[50005],g[50005];
int top(int i,int j){//斜率分子
	return (dp[i]+g[i]*g[i])-(dp[j]+g[j]*g[j]);
}
int down(int i,int j){//斜率分母
	return g[i]-g[j];
}
signed main(){
	cin>>n>>m;{
		for(int i=1;i<=n;i++)cin>>sum[i];
		sum[0]=dp[0]=0;
		for(int i=1;i<=n;i++){
			sum[i]+=sum[i-1];
			h[i]=sum[i]+i-1-m;
			g[i]=sum[i]+i;//预处理
		}
		int l=1,r=0;
		q[++r]=0;
		for(int i=1;i<=n;i++){
			while(l+1<=r&&top(q[l+1],q[l])<=2*h[i]*down(q[l+1],q[l]))l++;
			int j=q[l];
			int tmp=i-j-1+sum[i]-sum[j]-m;
			dp[i]=dp[j]+tmp*tmp;
			while(l+1<=r&&top(i,q[r])*down(q[r],q[r-1])<=top(q[r],q[r-1])*down(i,q[r]))r--;//如上
			q[++r]=i;
		}
		cout<<dp[n]<<'\n';
	}
	return 0;
}

3.P3628 [APIO2010]特别行动队

基础状态转移方程:dp[i]=dp[j]+a*(sum[i]-sum[j])*(sum[i]-sum[j])+b*(sum[i]-sum[j])+c;

开 始 愉 (lie)(kai) 地 推 式 子:

\(dp_j+a\times (sum_i-sum_j)\times (sum_i-sum_j)+b\times(sum_i-sum_j)+c \le dp_k+a\times (sum_i-sum_k)\times (sum_i-sum_k)+b\times(sum_i-sum_k)+c\)

经过爆展和一系列合并之后如下:

\(2\times a \times sum_i \le\frac{(dp_j+a\times sum_j\times sum_j-b\times sum_j)-(dp_k+a\times sum_k\times sum_k-b\times sum_k)}{sum_j-sum_k}\)

然后套用之前的模板(这次求最大值!不要弄反了!)

#include<bits/stdc++.h>
#define int long long//不开ll见祖宗
using namespace std;
int n,m,q[1000005],sum[1000005],dp[1000005],a,b,c;
int top(int i,int j){
	return (dp[i]+a*sum[i]*sum[i]-b*sum[i])-(dp[j]+a*sum[j]*sum[j]-b*sum[j]);
}
int down(int i,int j){
	return sum[i]-sum[j];
}
signed main(){
	cin>>n>>a>>b>>c;
	for(int i=1;i<=n;i++)cin>>sum[i];
		sum[0]=dp[0]=0;
		for(int i=1;i<=n;i++){
			sum[i]+=sum[i-1];
		}
		int l=1,r=0;
		q[++r]=0;
		for(int i=1;i<=n;i++){
			while(l+1<=r&&top(q[l+1],q[l])>=2*a*sum[i]*down(q[l+1],q[l]))l++;
			int j=q[l];
			dp[i]=dp[j]+a*(sum[i]-sum[j])*(sum[i]-sum[j])+b*(sum[i]-sum[j])+c;
			while(l+1<=r&&top(i,q[r])*down(q[r],q[r-1])>=top(q[r],q[r-1])*down(i,q[r]))r--;
			q[++r]=i;
		}
		cout<<dp[n]<<'\n';
	
	return 0;
}

跟之前代码差别不大,重在柿子。

Part.5 拓展习题讲解

之前都是简单的 \(O(n^2)\) 优化成 \(O(n)\)。

那么如果出现了更加复杂的情况呢?

例如 P2365 任务安排

高能预警

\(O(n^3)\) 十分套路,先来看 \(O(n^2)\)。

考虑费用提前,先摆上方程:

//待填坑,关于费用提前的说明

\(dp_j=\min(dp_j+sumT_i\times (sumF_i-sumF_j)+s\times (sumF_n-sumF_i);\)

有人会问:这么写 \(dp_i\) 根本就没有正确性啊?为什么对呢?

这就是费用提前法的高明之处了: \(dp_n\) 之前都是错的且答案偏大,到\(dp_n\) 就对了。

为什么呢?

请看到 \(s\times (sumF_n-sumF_i)\) 这一段,正常动态规划之所以慢,是因为要枚举分段算贡献,但是早加晚加反正都是全加,不如就挨个挨个加上,就能保证且仅能保证 \(dp_n\) 的正确性。

其实到这一步,因为 \(n\) 的范围比较水,实际上已经可以过掉了,但是为了操练斜率优化 (卡最优解,我们还是要推式子。

没有平方,应该还是好推的。

自行参考如下。

int top(register int i,register int j){
	return (dp[i]-s*sf[i])-(dp[j]-s*sf[j]);
}
int down(register int i,register int j){
	return sf[i]-sf[j];
}

接下来又是板子,由此可见当斜率优化加上各种奇怪优化等于毒瘤(划掉。

最后一个习题:P3648

作为 APIO 的原题,质量还是很高的。

基本状态转移方程:
f[i]=dp[j]+sum[j]*(sum[i]-sum[j]);

本来是二维,但是通过滚动数组的方式可以降维优化。

接下来,只要进行 \(k\) 次动态规划就能得到答案,当然,还要记一下方案。

因为是最后一道题,柿子和代码咕咕咕掉了((,有问题可以康康题解。

完结撒花!!1

标签:le,int,sum,笔记,times,斜率,优化,dp
From: https://www.cnblogs.com/Forever1507/p/17046755.html

相关文章

  • MySql学习笔记--进阶04
        ......
  • 网络流学习笔记
    我承认了,我粘的LiveDreamClassin里的图!我没学费用流!一·网络最大流1.\(EK\)这个只是铺垫(https://oi-wiki.org/graph/flow/max-flow/2.\(Dinic->O(n^2m)\)当多......
  • 【Python】爬虫笔记-从PyMySQL到DBUtils
    1.PyMySQL1.1基本使用PyMySQL是在Python3.x版本中用于连接MySQL服务器的一个库,Python2中则使用mysqldb。PyMySQL遵循Python数据库APIv2.0规范,并包含了pur......
  • RabbitMQ学习笔记06:Topics
    参考资料:RabbitMQtutorial-Topics—RabbitMQ  前言在上一篇博文中我们使用direct类型的exchange改善了我们的日志系统,但是它仍然有一定的限制,它没有办法基于多个......
  • Effective C++ 笔记
    EffectiveC++笔记Sec0Introduction本书的目的:如何有效运用C++,使软件易理解、易维护、可移植、可扩充、高效、并有预期行为提出的忠告分两类:一般性的设计策略,带有......
  • 【学习笔记】缓存
    缓存1.什么是缓存缓存是存在内存中的临时数据。将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用去磁盘(关系型数据库数据文件)上查询,从缓存中查询,从而提高查询效......
  • python:海量数据集分页优化
    学过Django框架的同学,一定都使用过Django框架的Paginator分页功能,今天我们要讨论的是关于使用Paginator进行大数据集分页时,它性能的优化问题。Paginator分页下面步入正题,首......
  • SEO初学者:10步优化你的网站(上)seo网站情况查询
    昨天给大家介绍了seo的意义和重要性,今天让我们一起看看10个基本的SEO初学者技巧,如何优化网站以增加流量。1. 研究关键词并使用尾词关键词在SEO中起着重要的作用。关键字表......
  • OpenGL ES 2.0编程指导阅读笔记(二)你好,三角形:OpenGL ES 2.0示例
    本章覆盖以下内容:用EGL创建屏上表面加载顶点和片元着色器创建程序对象,附加顶点和片元着色器,并链接程序对象设置视点清除colorbuffer渲染一个简单图元使colorbuff......
  • OpenGL ES 2.0编程指导阅读笔记(三)EGL介绍
    EGL能够管理绘图表面。EGL提供了以下机制:和本地窗口系统进行通信;查询可用的绘图表面类型和配置;创建绘图表面;在OpenGLES2.0和其他图形渲染API之间同步渲染;管理渲染......