首页 > 其他分享 >DP优化技巧-斜率优化(基础版)

DP优化技巧-斜率优化(基础版)

时间:2024-07-12 22:30:10浏览次数:16  
标签:return int double sum 斜率 1.0 优化 qy DP

DP优化技巧-斜率优化(基础版)

基本思路:


1.寻找出暴力DP转移方程式。例子:f[i]=min{f[j]+v[i]+v[j]+val(i,j)}
2.将方程式写成y=kx+b的形式,其中b为与i相关的项,y为与j相关的项,kx对应的是val(i,j)项,其中x对应的的是与j相关的,k对应的是与i相关的以及常数。例子:假设有转移方程f[i]=min{f[i],f[j]+sum[i]*sum[i]+sum[j]*sum[j]+2*sum[i]*sum[j]+C}(C为常数) 那么可以得到f[j]+sum[j]*sum[j]=2*sum[j]*sum[i]-f[i]+sum[i]*sum[i]+C 其中f[j]+sum[j]*sum[j]y项,sum[j]对应x项,2*sum[i]对应k项,-f[i]+sum[i]*sum[i]+Cb项。
3.建立单调队列进行维护凸包,凸包的知识在此挖个坑,届时会补上(不一定:P 而单调队列的push操作里的去除队尾的依据为新加入元素没有与队尾、队尾前一个元素组成一个下凸壳。如果队尾只有或只剩一个元素,则退出此操作。
4.在单调队列中进行二分,根据凸壳的知识,选择合适的点,对f[i]进行普通赋值。


易错点/注意到:

1.容易将一些无用的状态忽略,而没有对其赋最大/最小值,输入DP通病。
2.要搞清楚自己的k的符号,假如最终化简方程式形式为:b=y-kx,则求出来的k应该取相反数。
3.要根据k的值维护上凸包or下凸包。
4.注意单调队列的初始化。
5.注意队列里只有1or0个元素时,不应该进行判定操作,而是直接加入or进行其他操作。
6.多回头进行公式以及各个函数内容的检查与重算。


例题:

1.洛谷P4072 [SDOI2016] 征途

核心转移方程式:f[i][j]=f[i-1][k]+((sum[j]-sum[k])*m-sum[n])*((sum[j]-sum[k])*m-sum[n])

改写方程式:f[i][j]-(m*sum[j]-sum[n])*(m*sum[j]-sum[n])=(f[i-1][k]+m*m*sum[k]*sum[k]+2*m*sum[j]*sum[n])+2*m*m*sum[j]*sum[k]

补充说明:核心转移方程式中的k取值范围为:1<=k<j,改写方程式中的k为使f[i][j]最小的下标,经过单调队列优化得到

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
long long f[3010][3010],sum[3010];
double qy(int i,int j){double ans=f[i][j]+m*m*sum[j]*sum[j]+2*m*sum[j]*sum[n];return ans;}
double qx(int j){double ans=sum[j];return ans;}
double qk(int i,int j,int k,int l){return (qy(i,j)-qy(k,l))/(qx(j)-qx(l));}
long long val(int j,int i){return ((sum[i]-sum[j])*m-sum[n])*((sum[i]-sum[j])*m-sum[n]);}
struct que{
	int l,r,a[200010];
	void memsets(){l=1;r=0;}
	int size(){return r-l+1;}
	int front(){return a[l];}
	void pop(){l++;}
	void push(int i,int x){
		while (size()>=2&&(qk(i,a[r-1],i,a[r])>=qk(i,a[r],i,x))==true) r--;
		a[++r]=x;
	}
}q;
long long a[3010];
int main(){
	scanf("%d%d",&n,&m);	
	for (int i=1;i<=n;i++){scanf("%lld",&a[i]);sum[i]=sum[i-1]+a[i];}
	for (int i=1;i<=n;i++) f[1][i]=val(0,i);
	for (int i=2;i<=m;i++){
		q.memsets();
		for (int j=1;j<i;j++){
			f[i][j]=1e18;
		}
		for (int j=i;j<=n;j++){
			q.push(i-1,j-1);
			long double k=2.0*m*m*sum[j];
			while (q.size()>=2&&k>=qk(i-1,q.a[q.l],i-1,q.a[q.l+1])) q.pop();
			int o=q.front();
			f[i][j]=f[i-1][o]+val(o,j);
		}
	}
	printf("%lld",f[m][n]/m);
	return 0;
}

2.洛谷P3195 [HNOI2008] 玩具装箱

核心转移方程式:f[i][j]=min(j<i){f[j]+(sum[i]-sum[j]+i-j-1-L)*(sum[i]-sum[j]+i-j-1-L)}

s[i]=sum[i]+i,L1=L+1,得到:f[i]=min(j<i){f[j]+(s[i]-s[j]-L1)*(s[i]-s[j]-L1)}

改写方程式:f[i]-(s[i]-L1)*(s[i]-L1)=min(j<i){f[j]+s[j]*s[j]+2*s[j]*(L1-s[i])}

补充说明:j与上面的k类似

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n;
long long l,l1,a[100010],f[100010],sum[100010],s[100010];
double qy(int i){return 1.0*f[i]+1.0*s[i]*s[i];}
double qx(int i){return 1.0*s[i];}
double qk(int i,int j){double ans=1.0*(1.0*qy(i)-1.0*qy(j))/(1.0*qx(i)-1.0*qx(j));return ans;}
bool cmp(int i,int j,int k){return qk(i,j)<=qk(j,k);}
struct que{
	int l,r,b[200010];
	void memsets(){l=1;r=0;memset(b,0,sizeof(b));}
	int front(){return b[l];}
	void pop(){l++;}
	bool empty(){return r-l+1==0;}
	int size(){return r-l+1;}
	void push(int x){
		while (size()>=2&&cmp(x,b[r],b[r-1])==true) --r;
		b[++r]=x;
	}
}q;
int main(){
	scanf("%d%lld",&n,&l);l1=l+1;q.memsets();
	for (int i=1;i<=n;i++){scanf("%lld",&a[i]);sum[i]=sum[i-1]+a[i];s[i]=sum[i]+i;}
//	for (int i=1;i<=n;i++) printf("i=%d sum[%d]=%lld s[%d]=%lld\n",i,i,sum[i],i,s[i]);
	for (int i=1;i<=n;i++){
		q.push(i-1);
		if (i==1){f[i]=(a[1]-l)*(a[1]-l);}
		else{
			long long k=2*(s[i]-l1);
			while (q.size()>=2&&k>1.0*(qy(q.b[q.l+1])-qy(q.b[q.l]))/(qx(q.b[q.l+1])-qx(q.b[q.l]))){
				q.pop();
			}
			int j=q.front();f[i]=f[j]+(s[i]-s[j]-l1)*(s[i]-s[j]-l1);
		}
	}
	printf("%lld",f[n]);
	return 0;
}```
***

标签:return,int,double,sum,斜率,1.0,优化,qy,DP
From: https://www.cnblogs.com/mmyzzqx/p/18299494

相关文章

  • 大厂性能优化的10大顶级方案 (万字图文史上最全)
    文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录博客园版为您奉上珍贵的学习资源:免费赠送:《尼恩Java面试宝典》持续更新+史上最全+面试必备2000页+面试必备+大厂必备+涨薪必备免费赠送:《尼恩技术圣经+高并发系列PDF》,帮你实现技术自由,完成职业升级,薪......
  • zdppy+onlyoffice+vue3解决文档加载和文档强制保存时弹出警告的问题
    解决过程第一次排查最开始排查的是官方文档说的https://api.onlyoffice.com/editors/troubleshooting#key解决方案。参考的是官方的https://github.com/ONLYOFFICE/document-server-integration/releases/latest/download/Python.Example.zip基于Django的Python代码。......
  • DP
    SparkSpecialdottle_dpnote解决问题:分析问题性质转化问题用熟悉方法解决要记住OI不是要你发明算法,只是要找方法!"让我们揭露dp的本质"1容斥容斥是一种很重要的思想,容斥的目标是转化问题。虽然,我们看似将问题转化复杂了,但是每一个子集的计算却变简单了(不必......
  • RabbitMQ + JMeter组合,优化你的中间件处理方式!
     RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息中间件,它是基于Erlang语言编写的,并发能力强,性能好,是目前主流的消息队列中间件之一。 RabbitMQ的安装可参照官网(https://www.rabbitmq.com/),安装完以后启动管理服务,RabbitMQ提供强大的管理功能。 在使用Jmeter处理Rabbi......
  • NOIP DP
    NOIPDP本章选用题目重做的方法进行复习会选择有价值的题目重做1.数位DPP2602数字计数Trick典型转换:前缀和转换通过从高到第枚举数字的方法进行统计。这是很常见的限制数字范围的方法。P4127同态分布所以数位DP开始推导的时候通常是从暴力开始的,开始的时候就是枚举......
  • 10个Python函数参数进阶用法及代码优化
    目录1.默认参数值:让函数更加灵活2.关键字参数:清晰的调用方式3.*args:拥抱不确定数量的位置参数4.**kwargs:处理不确定数量的关键字参数5.参数解包:简化多参数的传递6.命名关键字参数:限制关键字参数7.局部变量与全局变量:理解作用域8.高级:装饰器(@decorator)9.Lambd......
  • TCP与UDP
    TCP(TransmissionControlProtocol)什么是TCP?TCP是一种面向连接的、可靠的传输层协议,用于在计算机网络中传输数据。它确保数据能够按照发送顺序到达目的地,并且不丢失,确保了数据的完整性和顺序性。详细解释:TCP在传输数据之前,首先在发送端和接收端之间建立连接。这个连接是通过......
  • ThreadPoolExector
    JavaThreadPool使用线程池的好处:减少资源的浪费:创建、销毁、切换线程需要消耗系统资源,通过使用线程池可以降低消耗。增加可管理度:通过线程池的同一管理,能够实现线程的更好的管理。提高相应速度:当任务到来时,无需在创建线程,直接就能对任务进行反馈Java线程池的使用线程池......
  • 性能优化之降低OverDraw
    参考:Unity_降低OverDraw提高性能-哔哩哔哩(bilibili.com)一个好用的overdraw分析工具·GameDev(gitbooks.io)Unity性能优化-Overdraw篇_unityoverdraw-CSDN博客 什么是OverdrawUnityOverdraw(超绘)是指在渲染过程中,同一个坐标的像素进行过多次绘画的现象,一般由以......
  • 基于VGG16特征提取与聚类优化的苹果分类系统开发与性能提升
    数据集链接:https://pan.baidu.com/s/1qQglNzAIkBNxdrwND0NLNQ?pwd=data 提取码:data1.目的任务:根据original_data样本数据,建立模型,对test的图片进行普通/其他苹果判断 1.数据增强,扩充确认为普通苹果的样本数量 2.特征提取,使用VGG16模型提取特征 3.图片批量处理 ......