#斜率优化#JZOJ 2204 洛谷 2900 BZOJ 1597 土地购买

本文探讨了一种解决矩形堆叠问题的高效算法,通过将矩形按宽度排序并利用单调队列优化动态规划过程,实现了O(n)的时间复杂度。文章详细介绍了算法的推导过程,包括如何消除冗余条件、建立DP方程以及实现代码。

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

题目


分析

首先把宽度 l l l按第一关键字从小到大排序,高度 h h h按第二关键字从大到小排序,用一个栈把存在 h [ x ] ≤ h [ y ] , l [ x ] ≤ l [ y ] h[x]\leq h[y],l[x]\leq l[y] h[x]h[y],l[x]l[y]的情况消掉,然后一个显然的性质可以发现最优情况肯定是选择连续的一段,那么可以这样列出dp方程
d p [ i ] = min ⁡ { d p [ j ] + h [ j + 1 ] ∗ l [ i ] } dp[i]=\min\{dp[j]+h[j+1]*l[i]\} dp[i]=min{dp[j]+h[j+1]l[i]}
k ( j &lt; k ) k(j&lt;k) k(j<k)优于 j j j,那么 d p [ j ] + h [ j + 1 ] ∗ l [ i ] ≥ d p [ k ] + h [ k + 1 ] ∗ l [ i ] dp[j]+h[j+1]*l[i]\geq dp[k]+h[k+1]*l[i] dp[j]+h[j+1]l[i]dp[k]+h[k+1]l[i]
那么化简可得 d p [ j ] − d p [ k ] ≥ ( h [ k + 1 ] − h [ j + 1 ] ) ∗ l [ i ] dp[j]-dp[k]\geq (h[k+1]-h[j+1])*l[i] dp[j]dp[k](h[k+1]h[j+1])l[i]
因为若 h [ k + 1 ] ≥ h [ j + 1 ] h[k+1]\geq h[j+1] h[k+1]h[j+1],那么 l [ k + 1 ] &gt; l [ j + 1 ] l[k+1]&gt;l[j+1] l[k+1]>l[j+1]也不符合消掉的情况,所以 h [ k + 1 ] &lt; h [ j + 1 ] h[k+1]&lt;h[j+1] h[k+1]<h[j+1]
d p [ j ] − d p [ k ] h [ k + 1 ] − h [ j + 1 ] ≤ l [ i ] \frac{dp[j]-dp[k]}{h[k+1]-h[j+1]}\leq l[i] h[k+1]h[j+1]dp[j]dp[k]l[i]
显然 l l l是单调递增的,所以维护下凸壳,单调队列优化,时间复杂度 O ( n ) O(n) O(n)


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
struct rec{
	int x,y;
	bool operator <(const rec &t)const{
	    return x<t.x||(x==t.x&&y>t.y);
	}
}a[50001];
long long dp[50001]; int n,top,head,tail,q[50001];
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]-dp[j])/(a[j+1].y-a[k+1].y);}
signed main(){
	n=iut();
	for (rr int i=1;i<=n;++i) a[i]=(rec){iut(),iut()};
	sort(a+1,a+1+n);
	for (rr int i=1;i<=n;++i){
		while (top&&a[top].y<=a[i].y) --top;
		a[++top]=a[i];
	}
	n=top,head=1,tail=0,q[++tail]=0;
	for (rr int i=1;i<=n;++i){
		while (head<tail&&slope(q[head],q[head+1])<=a[i].x) ++head;
		dp[i]=dp[q[head]]+1ll*a[i].x*a[q[head]+1].y;
		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、付费专栏及课程。

余额充值