bzoj1597: [Usaco2008 Mar]土地購買(斜率優化+Dp+單調佇列)

Hanks_o發表於2017-10-25

題目傳送門
真的舒服我終於第一次自己推出了斜率方程。。
真開心。

解法:
其實這道題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;
}

相關文章