zoukankan      html  css  js  c++  java
  • 【BZOJ】1597 [Usaco2008 Mar]土地购买

    【算法】DP+斜率优化

    【题意】n(n≤50000)块土地,长ai宽bi,可分组购买,每组代价为max(ai)*max(bi),求最小代价。

    【题解】

    斜率优化:http://www.cnblogs.com/ka200812/archive/2012/08/03/2621345.html

    因为对于土地x和y,若满足a[x]<=a[y]&&b[x]<=b[y],则x土地可无条件包含在y土地中,所以x土地可以忽略。

    于是对长度从小到大排序,第二关键字对宽度从小到大排序,处理掉可被包含块。

    则有a严格升序,b严格降序

    然后就是常规DP。

    状态转移方程:f[i]=min(f[j]+a[i]*b[j+1])(j:0~i-1)

    O(n^2)的效率显然不足,考虑斜率优化。

    下面只从代数上理解斜率优化。

    阶段i时对于决策j和k (j<k),当k更优时满足

    f[j]+a[i]*b[j+1]>f[k]+a[i]*b[k+1]

    即(f[j]-f[k])/(b[k+1]-b[j+1])<a[i]

    令d(j,k)=(f[j]-f[k])/(b[k+1]-b[j+1]),

    当d(j,k)<a[i]时,k决策优于j决策,决策的位置就会往后移;当d(j,k)>a[i]时,j决策优于k决策,决策的位置就会往前移。

    我们认为d(j,k)越小越容易把决策往后送,定义为更优,也就是斜率越小越优。

    于是维护一个决策队列,队列严格要求两两之间的d从最优到最劣,因为a[i]递增,当有一个新的a[i]时,前面的d(j,k)小的把决策后推,直到遇到大的就停下来,确定了最优决策。

    为什么要维护决策队列的d从最优到最劣?设决策j,k,l(j<k<l),因为若有d(j,k)>d(k,l),当a[i]<d(k,l)时,j>k>l,当d(k,l)<a[i]<d(j,k)时,j>k&&l>k,当a[i]>d(j,k)时,l>k>j。可以发现,无论何时决策k都不会被选择,故应当删去。

    斜率不是真实的,它只是比较优劣的工具,决策的优劣只与a[i]有关,而斜率只是比较两决策优劣与a[i]的关系。

    但是我们为什么要定义斜率的优劣?因为右项a[i]单调递增。

    所以删去k后,j和l之间会出现新的斜率,当a[i]和这个斜率发生大小变化时,j或l的选择将会改变。

    整个过程可以想象成维护下凸包,不再赘述。

    斜率优化题目的通法:

    1.分离:列出决策优劣比较式(i阶段,j<k且k更优时),分离i和j k,当i有关变量呈现单调性质时可以进行斜率优化,根据不等关系定义斜率的优劣,维护从最优到最劣的决策队列。

    2.决策:选取队头两个决策d(head,head+1),若d满足优劣式说明k更优,删除队头j,继续比较;若d不满足优劣式说明j更优,则j为当前最优决策。

    3.入队:选取队尾两个决策d1(tail-1,tail)和当先决策与队尾决策的d2(tail,i),若d2优于d1则删除队尾,继续比较,直到d2劣于d1就把i入队。

    【注意】long long=1ll*int*int,记得类型转换……。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int maxn=50010;
    long long f[maxn];
    int q[maxn],n;
    struct cyc{int x,y;}a[maxn],b[maxn];
    bool cmp(cyc a,cyc b)
    {return a.x<b.x||(a.x==b.x&&a.y<b.y);}
    double d(int j,int k)
    {return (f[j]-f[k])/(a[k+1].y-a[j+1].y);}//1.0*
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d%d",&b[i].x,&b[i].y);
        sort(b+1,b+n+1,cmp);
        int m=1;a[1]=b[1];
        for(int i=2;i<=n;i++)
        {
            while(b[i].y>=a[m].y&&m>0)m--;
            a[++m]=b[i];
        }
        n=m;
    //    for(int i=1;i<=n;i++)printf("%d %d
    ",a[i].x,a[i].y);
    //    f[0]=a[0].x=a[0].y=0;
        int head=0,tail=1;q[0]=0;
        for(int i=1;i<=n;i++)
        {
            while(tail-head>=2&&d(q[head],q[head+1])<a[i].x)head++;
            f[i]=f[q[head]]+1ll*a[i].x*a[q[head]+1].y;
    //        printf("f[%d]=%lld
    ",i,f[i]);
            while(tail-head>=2&&d(q[tail-1],q[tail-2])>d(q[tail-1],i))tail--;//ÕâÑùÉèÖöÓÁУ¬tail´¦Ã»ÓÐÊý×Ö¡­¡­ 
            q[tail++]=i;
        }
        printf("%lld",f[n]);
        return 0;
    }
    View Code
  • 相关阅读:
    微信红包实现算法
    Java中Redis简单入门
    Java之英格玛简单实现以及加密验证码的应用
    Java实现二维码
    String,StringBuffer,StringBuilder个人认为较重要的区别
    Java中Comparable和Comparator你知多少?
    Oracle
    maven下载及安装最详解
    破解路由器
    Activiti工作流学习-----基于5.19.0版本(8)
  • 原文地址:https://www.cnblogs.com/onioncyc/p/6113393.html
Copyright © 2011-2022 走看看