【知识点总结】斜率优化dp(待更新)

本文通过六道例题深入浅出地介绍了斜率优化DP这一高效算法技巧,旨在帮助读者理解如何利用斜率优化减少动态规划的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//今天有点晚了,先放几道例题

这张凸包的图可以方便感性理解斜率优化排点的原理:
(只有边缘的点有用)
只有边缘的点有用


EXAMPLES

(虽然说都比较水,但是相对难度还是有一些梯度。。)

1

[HNOI2008]玩具装箱TOY

题目传送门

#include<bits/stdc++.h>
using namespace std;
#define db double
#define int long long
const int maxn=5e4+10;
int n,L;
db s[maxn],f[maxn];
int head,tail,q[maxn];
inline db a(int i){return s[i]+i;}
inline db b(int j){return s[j]+j+L+1;}
inline db X(int j){return b(j);}
inline db Y(int j){return f[j]+b(j)*b(j);}

inline db slope(int i,int j){return (Y(i)-Y(j))/(X(i)-X(j));}
signed main(){
	scanf("%lld%lld",&n,&L);
	for(int i=1;i<=n;++i){
		scanf("%lf",&s[i]);
		s[i]+=s[i-1];
	}
	head=1;tail=1;
	for(int i=1;i<=n;++i){
		while(head<tail&&slope(q[head],q[head+1])<2*a(i))++head;
		//f[i]=Y(q[head])-2*a(i)*X(q[head])+a(i)*a(i);Îó²î½Ï´ó
		int j=q[head];
		f[i]=f[j]+(a(i)-b(j))*(a(i)-b(j));
		while(head<tail&&slope(q[tail-1],i)<slope(q[tail-1],q[tail]))--tail;
		q[++tail]=i;
	}
	printf("%lld",(long long)f[n]);
	return 0;
} 

2

HDU3507打印文章

博客点这里

题目传送门

#include<bits/stdc++.h>
#define pf printf
#define sf scanf
#define ll long long
using namespace std;
const int maxn=5e5+10;
ll f[maxn],s[maxn],n,m;
inline ll y(int a,int b){return f[a]+s[a]*s[a]-f[b]-s[b]*s[b];}
inline ll x(int a,int b){return s[a]-s[b];}
int q[maxn],head,tail=0;
/*
	f[i]=f[j]+(s[i]-s[j])^2+m
	f[j]+s[j]^2=2*s[i] *s[j] - s[i]^2 -m + f[i];
*/
signed main (){
	while(~sf("%d%d",&n,&m)){
		for(int i=1;i<=n;++i)sf("%lld",&s[i]),s[i]+=s[i-1];
		head=tail=1;q[head]=0;f[0]=0;
		for(int i=1;i<=n;++i){
			while(head<tail&&y(q[head+1],q[head])<=2*s[i]*x(q[head+1],q[head]))++head;
			int j=q[head];
			f[i]=f[j]+(s[i]-s[j])*(s[i]-s[j])+m;
			while(head<tail&&y(q[tail],q[tail-1])*x(i,q[tail])>=y(i,q[tail])*x(q[tail],q[tail-1]))--tail;
			q[++tail]=i;
		}
		cout<<f[n]<<"\n";
	}
	

	   return 0;
}


3

[zjoi2007]仓库建设

题目传送门

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define sf scanf
#define pf printf
#define db double
#define re register
#define gc getchar()
#define in rd()
inline int rd(){
	int num=0;char c=gc;
	for(;!isdigit(c);c=gc);
	for(;isdigit(c);c=gc)num=(num<<1)+(num<<3)+(c^48);
	return num;
}
const int maxn=1e6+10;
//Ws[i]= w[i]*s[i] (1-i)
//s[i]--(1-i)  w[i]--(1-i)
int w[maxn],s[maxn],Ws[maxn],n,c[maxn],f[maxn];
int head,tail,q[maxn];
/*
	f[i]=p[j]+s[i]*(w[i]-w[j])-(Ws[i]-Ws[j])

	p[j]+Ws[j]=s[i]*w[j]+f[i]+Ws[i]-s[i]*w[i]

*/
inline db slope(int a,int b){return (f[a]+Ws[a]-f[b]-Ws[b])*1.00/(w[a]-w[b])*1.00;}
signed main(){
	n=in;
	for(int re i=1;i<=n;++i){
		s[i]=in;w[i]=in;c[i]=in;
		Ws[i]+=Ws[i-1]+w[i]*s[i],w[i]+=w[i-1];
	}
	head=tail=1;
	for(int re i=1;i<=n;++i){
		while(head<tail&&slope(q[head+1],q[head])<=s[i]*1.00)++head;
		int j=q[head];
		f[i]=f[j]+s[i]*(w[i]-w[j])-(Ws[i]-Ws[j])+c[i];
		while(head<tail&&slope(q[tail-1],q[tail])>=slope(i,q[tail]))--tail;
		q[++tail]=i;
	}
	cout<<f[n];
	return 0;
} 

4

[Apio2010]特别行动队

题目传送门

#include<bits/stdc++.h>
#define pf printf
#define sf scanf
#define re register
#define ll long long
#define gc getchar()
#define db double
#define in rd()
using namespace std;
const int maxn=1e6+10;
inline ll rd(){
	int num=0,f=1;char c=gc;
	for(;!isdigit(c);c=gc)if(c=='-')f=-1;
	for(;isdigit(c);c=gc)num=(num<<1)+(num<<3)+(c^48);
	return num*f;
}
ll s[maxn],a,b,c,f[maxn],n;
int q[maxn],head,tail;
inline db slope(int x,int y){return (f[x]+a*s[x]*s[x]-f[y]-a*s[y]*s[y])*1.00/(s[x]-s[y])*1.00;}
/*
	f[i]=a*(s[i]-s[j])*(s[i]-s[j])+b*(s[i]-s[j])+c+f[j]
	
	f[j]+a*s[j]*s[j]=(2*a*s[i]+b)*s[j]-b*s[i]-a*s[i]*s[i]-c+f[i]
	
*/


signed main (){
	n=in;a=in,b=in,c=in;
	for(int re i=1;i<=n;++i)s[i]=in,s[i]+=s[i-1];
	head=tail=0;
	for(int re i=1;i<=n;++i){
		while(head<tail&&slope(q[head],q[head+1])>=(2.00*a*s[i]+b*1.00))++head;
		int j=q[head];
		f[i]=a*(s[i]-s[j])*(s[i]-s[j])+b*(s[i]-s[j])+c+f[j];
		while(head<tail&&slope(q[tail],q[tail-1])<=slope(q[tail],i))--tail;
		q[++tail]=i;
	}	
	cout<<f[n];


	   return 0;
}

5

[SDOI2012]任务安排

题目传送门

#include<bits/stdc++.h>
#define pf printf
#define sf scanf
#define ll long long
#define int long long
using namespace std;
const int maxn=3e5+10;
ll f[maxn];
int s[maxn],t[maxn],S,n;
int head,tail,q[maxn];
/*
	f[j]=(t[i]+S)*s[j]-S*s[n]-s[i]*t[i]+f[i];
	f[i]=f[j]+(s[i]-s[j])*t[i]+ S*(s[n]-s[j])
	t[i]+S ²»µ¥µ÷£¬ËùÒÔ¶þ·Ö 
*/

inline int find(int k){
	if(head==tail)return q[head];
	int l=head,r=tail;
	while(l<r){
		int mid=(l+r)>>1;
		if((f[q[mid+1]]-f[q[mid]])<=k*(s[q[mid+1]]-s[q[mid]]))l=mid+1;
		else r=mid;
	}
	return q[l];
}
signed main (){
	sf("%lld%lld",&n,&S);
	for(int i=1;i<=n;++i)sf("%lld%lld",&t[i],&s[i]),t[i]+=t[i-1],s[i]+=s[i-1];
	head=tail=1;
	f[0]=0;s[0]=0;
	for(int i=1;i<=n;++i){
		int j=find(t[i]+S);
		f[i]=f[j]+1ll*(s[i]-s[j])*t[i]+1ll*S*(s[n]-s[j]);
		while(head<tail&&(f[q[tail]]-f[q[tail-1]])*(s[i]-s[q[tail]])>=(f[i]-f[q[tail]])*(s[q[tail]]-s[q[tail-1]]))--tail;
		q[++tail]=i;	
	}
	cout<<f[n];

	   return 0;
}

6

Cats Transport

题面及博客

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e5+10;
int dp[maxn][150],s[maxn],a[maxn];
int h[maxn];
int n,m,P;
int q[maxn];
inline int Y(int i,int k){
	return dp[i][k-1]+s[i];
}
signed main(){
	scanf("%d%d%d",&n,&m,&P);
	for(int i=2;i<=n;++i)scanf("%d",&h[i]),h[i]+=h[i-1];
	for(int i=1;i<=m;++i){
		int x,y;scanf("%d%d",&x,&y);
		a[i]=y-h[x];
	}
	sort(a+1,a+m+1);
	for(int i=1;i<=m;++i)s[i]=s[i-1]+a[i];
	memset(dp,0x5f,sizeof dp);
	dp[0][0]=0;
	for(int k=1;k<=P;++k){
		int head=1,tail=1;
		for(int i=1;i<=m;++i){
			while(head<tail&&(Y(q[head+1],k)-Y(q[head],k))<=(q[head+1]-q[head])*a[i])++head;
			int j=q[head];
			dp[i][k]=dp[j][k-1]+(i-j)*a[i]-s[i]+s[j];
//			cerr<<j<<endl;
//			cerr<<i<<" "<<k<<" "<<dp[i][k]<<endl;
			
			while(head<tail&&(Y(q[tail-1],k)-Y(q[tail],k))*(q[tail-1]-i)>=(Y(q[tail-1],k)-Y(i,k))*(q[tail-1]-q[tail]))--tail;
			q[++tail]=i;
		}
	}
	printf("%lld",dp[m][P]);
	return 0;
}
 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值