洛谷P2900 [USACO08MAR]土地征用Land Acquisition(斜率优化)

题意

约翰准备扩大他的农场,眼前他正在考虑购买N块长方形的土地。如果约翰单买一块土 地,价格就是土地的面积。但他可以选择并购一组土地,并购的价格为这些土地中最大的长 乘以最大的宽。比如约翰并购一块3 × 5和一块5 × 3的土地,他只需要支付5 × 5 = 25元, 比单买合算。 约翰希望买下所有的土地。他发现,将这些土地分成不同的小组来并购可以节省经费。 给定每份土地的尺寸,请你帮助他计算购买所有土地所需的最小费用。

 

题解

  一道斜率优化

  我们先考虑一下,如果某一块土地的长和宽小于等于另一块土地,那么这两块土地可以合并,这块土地可以和另一块一起买

  所以我们按长为第一关键字,宽为第二关键字,升序排序,那么如果一块土地的长和宽都小于右边的,我们就可以把这块土地忽略不计(相当于合并到另一块里),那么最后留下的土地一定是长度递增,宽度递减的。那么在这一种情况下我们选取的土地一定是连续的一段。为什么呢?假设有$k<j<i$三块土地,那么$w[k]<w[j]<w[i]$且$h[k]>h[j]>h[i]$,那么一起买,花费为$w[i]*h[k]$,而如果不是一起买,即选取的不是连续的区间,比如买$i,k$再买$j$,那么花费就是$w[i]*h[k]+w[j]*h[j]$,不如之前的方案优

  然后就可以列出转移方程$$dp[i]=min\{dp[j]+h[j+1]*w[i]\}$$

  假设$j$比$k$更优,则有$$dp[j]+h[j+1]*w[i]<dp[k]+h[k+1]*w[i]$$

  $$dp[j]-dp[k]<h[k+1]*w[i]-h[j+1]*w[i]$$

  $$\frac{dp[j]-dp[k]}{h[k+1]-h[j+1]}<w[i]$$

  然后就可以上斜率优化了

 1 //minamoto
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<algorithm>
 5 #define ll long long
 6 using namespace std;
 7 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
 8 char buf[1<<21],*p1=buf,*p2=buf;
 9 inline int read(){
10     #define num ch-'0'
11     char ch;bool flag=0;int res;
12     while(!isdigit(ch=getc()))
13     (ch=='-')&&(flag=true);
14     for(res=num;isdigit(ch=getc());res=res*10+num);
15     (flag)&&(res=-res);
16     #undef num
17     return res;
18 }
19 const int N=50005;
20 struct node{
21     int x,y;
22     inline bool operator <(const node b)const
23     {return x==b.x?y<b.y:x<b.x;}
24 }a[N],b[N];
25 ll f[N];int n,tot,h,t,q[N];
26 inline double slope(int j,int k){return (f[k]-f[j])/(b[j+1].y-b[k+1].y);}
27 int main(){
28     //freopen("testdata.in","r",stdin);
29     n=read();
30     for(int i=1;i<=n;++i) a[i].x=read(),a[i].y=read();
31     sort(a+1,a+1+n);
32     for(int i=1;i<=n;++i){
33         while(tot&&a[i].y>=b[tot].y) --tot;
34         b[++tot]=a[i];
35     }
36     for(int i=1;i<=tot;++i){
37         while(h<t&&slope(q[h],q[h+1])<b[i].x) ++h;
38         f[i]=f[q[h]]+1ll*b[q[h]+1].y*b[i].x;
39         while(h<t&&slope(q[t],q[t-1])>slope(q[t-1],i)) --t;q[++t]=i;
40     }
41     printf("%lld\n",f[tot]);
42     return 0;
43 }

 

转载于:https://www.cnblogs.com/bztMinamoto/p/9545913.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值