BZOJ 1597: [Usaco2008 Mar]土地购买【斜率优化】

28 篇文章 0 订阅
9 篇文章 0 订阅

Description

农夫John准备扩大他的农场,他正在考虑N (1 <= N <= 50,000) 块长方形的土地. 每块土地的长宽满足(1 <= 宽 <= 1,000,000; 1 <= 长 <= 1,000,000). 每块土地的价格是它的面积,但FJ可以同时购买多快土地. 这些土地的价格是它们最大的长乘以它们最大的宽, 但是土地的长宽不能交换. 如果FJ买一块3x5的地和一块5x3的地,则他需要付5x5=25. FJ希望买下所有的土地,但是他发现分组来买这些土地可以节省经费. 他需要你帮助他找到最小的经费.

题解

首先,我们可以发现,如果一个矩阵的长和宽都小于等于令一个矩阵的长和宽,那么这个矩阵就被废掉了。这样处理掉有什么好处呢?显然,如果按照长度从小到大排序,那么宽度一定是递减的,这样,我们每次购买的就一定是一个区间,就可以方便的写出DP的转移方程。f[i]=min(f[i],f[j]+a[j+1].y*a[i].x),然后裸写DP肯定是不可以的,所以就用斜率优化搞一下。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 50006
#define LL long long
using namespace std;
inline char nc(){
    static char buf[100000],*i=buf,*j=buf;
    return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;
}
inline int _read(){
    char ch=nc();int sum=0;
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum;
}
struct point{
    LL x,y;
    LL operator *(const point&b)const{return x*b.y-y*b.x;}
    point operator -(const point&b)const{point c;c.x=x-b.x;c.y=y-b.y;return c;}
}que[maxn];
struct data{
    int x,y;
    bool operator <(const data&b)const{return x<b.x||(x==b.x&&y<b.y);}
}a[maxn],b[maxn];
int n,m,a0;
LL f[maxn];
bool vis[maxn];
LL cross(point x,point y,point z){
    return (z-y)*(y-x);
}
int main(){
    freopen("acquire.in","r",stdin);
    freopen("acquire.out","w",stdout);
    n=_read();
    for(int i=1;i<=n;i++)a[i].x=_read(),a[i].y=_read();
    int Max=0;
    sort(a+1,a+1+n);
    for(int i=n;i>=1;i--) if(a[i].y>Max)vis[i]=1,Max=a[i].y;
    for(int i=1;i<=n;i++) if(vis[i])b[++m]=a[i];
    int i=1;
    while(i<=m){
        int j=i;
        while(j<=m&&b[j].y<=b[j+1].y)j++;
        a[++a0].x=b[j].x;a[a0].y=b[j].y;i=j+1;
    }
    int hed=1,tal=1;que[1].y=0;que[1].x=-a[1].y;
    for(int i=1;i<=a0;i++){
        point x;x.x=1;x.y=a[i].x;
        while(hed<tal&&((que[hed+1]-que[hed])*x>=0))hed++;
        f[i]=que[hed].y-a[i].x*que[hed].x;x.x=-a[i+1].y;x.y=f[i];
        while(hed<tal&&(cross(que[tal-1],que[tal],x)>=0))tal--;
        que[++tal]=x;
    }
    printf("%lld",f[a0]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值