bzoj1597: [Usaco2008 Mar]土地购买(斜率优化+Dp+单调队列)

31 篇文章 0 订阅
6 篇文章 0 订阅

题目传送门
真的舒服我终于第一次自己推出了斜率方程。。
真开心。

解法:
其实这道题Dp还是挺好想的。
Dp方程大概就是f[j]+i到j所有的土地一组的花费。
其实看到斜率优化的题我一般都想怎么去搞单调性。。
排序。。
先按照长排序一遍。(升降随意,我从小到大排的)
对于每块土地i,j<i
那么i的长肯定大于等于j的长(排了序)
如果i的宽也大于等于j的宽的话,j就没用了。。
因为它跟i一组就完全没贡献啊,所以我们可以把他删掉。
这样就会出现。。
前面的土地长都比后面的小。
前面的土地宽都比后面的大。
单调性出来了!!!

所以从j到i分为一组的花费就是j的宽*i的长。
所以Dp方程就为min(f[i],f[j]+a[j+1].r*a[i].l) (l为长,r为宽)

设j<k 且j对于i更优,有方程:
f[j]+a[j+1].r*a[i].l<f[k]+a[k+1].r*a[i].l
(f[k]-f[j])/(a[j+1].r-a[k+1].l)<a[i].l

然后就按照斜率优化去搞呗。

代码实现:

#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
typedef long long ll;
using namespace std;
struct node {
    ll l,r;
}a[110000],b[110000];
ll f[110000];
bool cmp(node n1,node n2) {
    if(n1.l!=n2.l)
        return n1.l<n2.l;
    return n1.r<n2.r;
}
double slop(int j,int k) {
    return double(f[k]-f[j])/double(b[j+1].r-b[k+1].r);
}
int list[110000],head,tail;
int main() {
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld%lld",&a[i].l,&a[i].r);
    sort(a+1,a+1+n,cmp);
    int len=0;
    for(int i=1;i<=n;i++) {
        while(len>0&&b[len].r<=a[i].r)
            len--;
        len++;b[len]=a[i];  //b为删除没用状态后的所有土地
    }
    head=1;tail=1;list[1]=0;
    for(int i=1;i<=len;i++) {
        while(head<tail&&slop(list[head],list[head+1])<b[i].l)
            head++;
        int t=list[head];
        f[i]=f[t]+b[t+1].r*b[i].l; 
        while(head<tail&&slop(list[tail-1],list[tail])>slop(list[tail],i))
            tail--;
        list[++tail]=i;
    }
    printf("%lld\n",f[len]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值