斜率优化bzoj 1597

本文介绍了一种使用斜率优化动态规划方法解决地块合并问题的算法。通过分析地块的长宽值,实现地块的最优合并,以达到最小化成本的目标。文章详细解释了决策过程,并给出了具体的时间复杂度分析及代码实现。

题目链接:

https://www.lydsy.com/JudgeOnline/problem.php?id=1597

看到题目,时间复杂度至少是O(NlogN)O(NlogN)O(NlogN)O(Nsqrt(N))O(Nsqrt(N))O(Nsqrt(N))
前者的概率较大,此题有计算内乘积,应该与分块无关(早就知道是斜率优化

假设长为aaa值,宽为bbb
观察题目, 可以猜想一下决策会不会有连续性,猜想决策可能与排序有关,先按长 (aaa) 排序。在思考一会,就会发现该猜想是对的,因为每个总方案中,如果存在一组土地aaa值不连续,那该方案一定不是唯一的最优解。

证明:假设存在一个方案使得将土地按aaa值升序排列后存在i,j,ki,j,ki,j,k使得ai<aj<aka_{i}<a_{j}<a_{k}ai<aj<aki,ki,ki,k分在一组中,而jjj在另一组中。若bj<bib_j<b_ibj<bi,则将i,j,ki,j,ki,j,k分在一组肯定会更优,因为其他不变,把jjj移动到i,ki,ki,k这一组,jjj原来的那组值会更小,而i,j,ki,j,ki,j,k这一组不会变,因为jjj的长和宽在这一组中均不为最大值。同理,可证明另一种情况。(有兴趣可以自行证明)

这样,我们就可以先按aaa值排序。又因为如果某一块土地bbb值比任何一块在它右边的土地小,那么一个最优解一定是将这块土地与那块土地合并,因为这样不会产生任何影响,就可以去除满足这个条件的土地

在这一步后,整个数组也就是aaa值递增,bbb值递减,那么简单看一下就知道可以dp了
f[i]f[i]f[i]为前iii块土地的最小花费
方程就是f[i]=f[j]+b[j+1]∗a[i]f[i]=f[j]+b[j+1]*a[i]f[i]=f[j]+b[j+1]a[i]
看到乘积,自然想到斜率优化,pi(−b[j+1],f[i]),ki=a[i]p_{i}(-b[j+1],f[i]),k_{i}=a[i]pi(b[j+1],f[i])ki=a[i]
就可以愉快的斜率优化了,时间复杂度O(N)O(N)O(N)

Code:Code:Code:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN=50000;
struct w{
	int a,b;
}a[MAXN+10],b[MAXN+10];
struct po{
	int x,y;
}s[MAXN+10];
int f[MAXN+10],q[MAXN+10],fr,ed;
inline bool cmp(const w&,const w&);
inline bool cmp_slope(po,po,int);
inline bool cmp_slope2(po,po,po);
inline int read();

signed main(){
	freopen ("1597.in","r",stdin);
	freopen ("1597.out","w",stdout);
	int n=read();
	for (register int i=1;i<=n;++i){
		a[i].a=read();
		a[i].b=read();
	}
	stable_sort(a+1,a+n+1,cmp);
	for (register int i=n;i>=1;--i){
		if (a[i].b > a[q[fr]].b)	q[++fr]=i;	
	}
	n=fr;	fr=0;
	for (register int i=1;i<=n;++i)	b[i]=a[q[n-i+1]];
	for (register int i=1;i<=n;++i)	a[i]=b[i];
	s[0].y=0;	s[0].x=-a[1].b;
	for (register int i=1;i<=n;++i){
		int k=a[i].a;
		while (fr>ed && cmp_slope(s[q[ed]],s[q[ed+1]],k))	++ed;
		f[i]=f[q[ed]]-s[q[ed]].x*a[i].a;
		s[i].y=f[i];	s[i].x=-a[i+1].b;
		while (fr>ed && cmp_slope2(s[q[fr-1]],s[q[fr]],s[i]))	--fr;
		q[++fr]=i;
	}
	printf("%lld\n",f[n]);
	return 0;
}

inline int read(){
	int x=0;
	char c=getchar();
	while (!isdigit(c))c=getchar();
	while (isdigit(c))x=(x<<3)+(x<<1)+(c&15),c=getchar();
	return x;
}

inline bool cmp(const w &x,const w &y){
	return x.a<y.a;
}

inline bool cmp_slope(po x,po y,int k){
	return ((double)(y.y-x.y))/((double)(y.x-x.x)) <= (double)k;
}

inline bool cmp_slope2(po x,po y,po z){
	return ((double)(y.y-x.y))/((double)(y.x-x.x)) >= ((double)(z.y-y.y))/((double)(z.x-y.x));	
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值