#斜率优化,单调队列#洛谷 2120 BZOJ 1096 仓库建设

本文探讨了在动态规划(DP)中应用凸包优化来提高算法效率的方法。通过维护下凸壳,使得DP状态转移方程能在O(logn)的时间复杂度内完成。文章详细解析了如何通过前缀和技巧简化DP方程,并给出了完整的C++实现代码。

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

题目


分析

dp方程显然是
d p [ i ] = min ⁡ { d p [ j ] + ∑ k = j + 1 i p [ k ] ( x [ i ] − x [ k ] ) } + c [ i ] dp[i]=\min\{dp[j]+\sum_{k=j+1}^ip[k](x[i]-x[k])\}+c[i] dp[i]=min{dp[j]+k=j+1ip[k](x[i]x[k])}+c[i]
考虑维护 p [ i ] x [ i ] , p [ i ] p[i]x[i],p[i] p[i]x[i],p[i]的前缀和 s , s u m s,sum s,sum,那么式子可以化成
d p [ i ] = min ⁡ { d p [ j ] + x [ i ] ( s u m [ i ] − s u m [ j ] ) − ( s [ i ] − s [ j ] ) } dp[i]=\min\{dp[j]+x[i](sum[i]-sum[j])-(s[i]-s[j])\} dp[i]=min{dp[j]+x[i](sum[i]sum[j])(s[i]s[j])}
k ( j &lt; k ) k(j&lt;k) k(j<k)更优于 j j j,那么
d p [ j ] + x [ i ] ( s u m [ i ] − s u m [ j ] ) − ( s [ i ] − s [ j ] ) &gt; d p [ k ] + x [ i ] ( s u m [ i ] − s u m [ k ] ) − ( s [ i ] − s [ k ] ) dp[j]+x[i](sum[i]-sum[j])-(s[i]-s[j])&gt;dp[k]+x[i](sum[i]-sum[k])-(s[i]-s[k]) dp[j]+x[i](sum[i]sum[j])(s[i]s[j])>dp[k]+x[i](sum[i]sum[k])(s[i]s[k])
那么简化可以得到
d p [ j ] − x [ i ] s u m [ j ] + s [ j ] &gt; d p [ k ] − x [ i ] s u m [ k ] + s [ k ] dp[j]-x[i]sum[j]+s[j]&gt;dp[k]-x[i]sum[k]+s[k] dp[j]x[i]sum[j]+s[j]>dp[k]x[i]sum[k]+s[k]
那么 d p [ k ] − x [ i ] s u m [ k ] + s [ k ] &lt; d p [ j ] − x [ i ] s u m [ j ] + s [ j ] dp[k]-x[i]sum[k]+s[k]&lt;dp[j]-x[i]sum[j]+s[j] dp[k]x[i]sum[k]+s[k]<dp[j]x[i]sum[j]+s[j]
d p [ k ] − d p [ j ] + s [ k ] − s [ j ] s u m [ k ] − s u m [ j ] &lt; x [ i ] \frac{dp[k]-dp[j]+s[k]-s[j]}{sum[k]-sum[j]}&lt;x[i] sum[k]sum[j]dp[k]dp[j]+s[k]s[j]<x[i]
由于距离是单调递增的,所以维护下凸壳


代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=1000001;
int n,dis[N],q[N],c[N]; long long sum[N],s[N],dp[N];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline double slope(int j,int k){
	return 1.0*(dp[k]+sum[k]-dp[j]-sum[j])/(s[k]-s[j]);
}
signed main(){
	n=iut();
	for (rr int i=1;i<=n;++i){
		dis[i]=iut(),sum[i]=iut(),c[i]=iut(),
		s[i]=s[i-1]+sum[i],sum[i]=sum[i-1]+sum[i]*dis[i];
	}
	rr int head=1,tail=0; q[++tail]=0;
	for (rr int i=1;i<=n;++i){
		while (head<tail&&slope(q[head],q[head+1])<dis[i]) ++head;
		dp[i]=dp[q[head]]+c[i]+(s[i]-s[q[head]])*dis[i]-sum[i]+sum[q[head]];
		while (head<tail&&slope(q[tail-1],q[tail])>slope(q[tail],i)) --tail;
		q[++tail]=i; 
	}
	return !printf("%lld",dp[n]);
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值