斜率优化

Land Acquisition[土地并购](USACO Mar08)

Task:

有n个长方形土地,已知各自的长与宽,并购两块土地的费用是两块土地长的较大值与较小值乘积,单块土地的费用即为长乘宽,求购置所有土地的费用。(n<=50000)

Solution:

第一道斜率优化题…
首先如果直接dp的话显然是无法转移的,所以先按长sort一波,然后宽比上一个小的就可以直接忽略不计了,因此宽是单调上升的。我们可以简单的写出一个朴素的dp方程: dp[i]=dp[j]+Y[i]X[j+1] 这个方程显然是 O(n2) 的,无法通过,于是就有了斜率优化。
假设 j<k ,我们看看当前的dp[i]如果从k转移优于j,那么就会得到:

dp[k]+Y[i]X[k+1]<=dp[j]+Y[i]X[j+1]
可化为:
dp[k]dp[j]<=Y[i](X[j+1]X[i+1])
即:
g(k,j)=dp[k]dp[j]X[j+1]X[i+1]<=Y[i]
因此,我们就知道了如果对于 j<k ,如果 g(k,j)<=Y[i] ,那么就可以丢弃 j 。显然为了保证决策最优,这个斜率的表达式是单调的。可以单调队列优化:
- 首先判断 g(Q[L+1],Q[L])<=Y[i] 如果成立,则丢弃 Q[L]
- 用当前最优解 Q[L] 来更新dp[i]
- 如果 g(i,Q[R])<=g(Q[R],Q[R1]) 则丢弃 Q[R]
- 将i加入到队列中
于是我们就用 O(nlogn) 的复杂度完成了本题(dp复杂度 O(n) )

#include<stdio.h>
#include<iostream>
#include<algorithm>
#define ll long long
#define M 50005
using namespace std;
struct Node{
    int x,y;
    bool operator <(const Node &a)const{
        return x>a.x;
    }
}A[M],B[M];
ll dp[M];
int Q[M];
double g(int k,int j){//j<k
    return 1.0*(dp[k]-dp[j])/(B[j+1].x-B[k+1].x);
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d %d",&A[i].x,&A[i].y);
    sort(A+1,A+n+1);
    int tot=0;
    for(int i=1;i<=n;i++){
        if(A[i].y<=B[tot].y)continue;
        B[++tot]=A[i];
    }
    int L=0,R=-1;
    Q[++R]=0;
    for(int i=1;i<=tot;i++){
        while(L<R&&g(Q[L+1],Q[L])<=B[i].y)L++;
        int id=Q[L];
        dp[i]=dp[id]+1LL*B[i].y*B[id+1].x;
        while(L<R&&g(i,Q[R])<=g(Q[R],Q[R-1]))R--;
        Q[++R]=i;
    }
    cout<<dp[tot]<<endl;
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值