BZOJ 1597:[Usaco2008 Mar]土地购买(斜率优化)

本文解决了一个关于如何最优分组矩形以最小化购买成本的问题。通过去除冗余矩形并利用斜率优化的动态规划算法,实现了高效求解。

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

BZOJ 1597:[Usaco2008 Mar]土地购买

题意概述:

n(1<=n<=5* 10^4)个矩形,多个矩形购买价格为其长宽(1*10^6)的最值乘积,求如何分组使得代价最小.

题目分析:

1.设矩形长宽为x,y;若有xi<=xj&&yi<=yj,那么i号矩形是无用的(可以被j号矩形包括,对答案无贡献),可以以xi< xj||(xi==xj&&yi< yj)排序.

2.排序以后可以发现,剩下的矩形必定是x递增,y递减的;那么可以定义数组ans,那么转移方程为

3.显然这个方法的时间复杂度是O(n^2)的,而n<=5* 10^4,那么假设当前为i号矩形,若要使j号能比k号将i更优化,则有

式子左边就得到了一个类似于斜率的东西,那么只要斜率< x[i],k号矩形就是无用的,不用从这里转移过来.

4.既然已经推导到了式子,那么就可以用一个斜率单调递减队列que来维护(que中存储编号):对于i号位置的ans要转移更新时,比较队首的两个元素斜率,若满足

则队首元素可以弹出,继续往下找;
完成更新之后的i号要进队的时候,比较队尾的两个元素斜率和队尾与当前点的斜率,若满足

则队尾元素可以弹出,继续往上找.

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

typedef long long ll;
const int maxn=500000+10;

struct Land {
    int x,y;
    bool operator < (const Land& rhs) const {
        return x<rhs.x||(x==rhs.x&&y<rhs.y);
    }
    void input() {
        scanf("%d%d",&x,&y);
    }
}land[maxn],st[maxn];

ll ans[maxn];
int que[maxn];

double slope(int a,int b)
{
    return 1.0*(ans[b]-ans[a])/(st[a+1].y-st[b+1].y);
}

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) land[i].input();
    sort(land+1,land+n+1);
    int top=0;
    for(int i=1;i<=n;i++) {//去除无用矩形
        while(top&&land[i].y>=st[top].y) --top;
        st[++top]=land[i];
    }
    int head=0,tail=0;
    for(int i=1;i<=top;i++) {//斜率优化dp转移
        while(head<tail&&slope(que[head],que[head+1])<st[i].x) ++head;//由于x递增,所以之前删去的矩形之后一定不用其转移
        ans[i]=ans[que[head]]+(ll)st[que[head]+1].y*st[i].x;
        while(head<tail&&slope(que[tail-1],que[tail])>slope(que[tail],i)) --tail;//维护斜率单调递减
        que[++tail]=i;
    }
    printf("%lld",ans[top]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值