zoukankan      html  css  js  c++  java
  • 斜率优化dp(POJ1180 Uva1451)

    学这个斜率优化dp却找到这个真心容易出错的题目,其中要从n倒过来到1的确实没有想到,另外斜率优化dp的算法一开始看网上各种大牛博客自以为懂了,最后才发现是错了。

    不过觉得看那些博客中都是用文字来描述,还是应该用画图来表示更容易让人明白,不过时间不太够,且网上该题解法到处都是,就不累赘了。

    代码才20几行真爽


    更为详细的描述参见:

    《浅谈数形结合思想在信息学竞赛中的应用》 周源

     POJ 1180 

    稍微加深自己的印象

    一般来说,斜率优化是从一些特殊数据的一些类似斜率的性质上找出其单调性。对于普通动态规划方程
    例如 poj1180(不考虑反向来讨论)
    F[i]=min(F[j]+(s+sumt[i]-sumt[j])*sumf[i]) 
    因此我们可以考虑
    在i之前
    在k<j<i时,考虑使
    F[j]+(s+sumt[i]-sumt[j])*sumf[i]<F[k]+(s+sumt[i]-sumf[k])*sumf[i]
    化简后可得到
    (F[j]-F[k])/(sumt[j]-sumt[k])<sumf[i]
    可以类比斜率,如果满足这个条件,那么决策j就比决策k更好,因此我们令
    ratio(k,j)=(F[j]-F[k])/(sumt[j]-sumt[k])
    当满足ratio(k,j)<sumf[i]则有j好于k
    而sumf[i]又是递增的,可以考虑单调队列。
    那应该用一个怎样的单调队列来维护最优值呢?
    什么值是不需要的?
    当ratio(l,k)>ratio(k,j)  (l<k<j)时
    如果sumf[i]<ratio(k,j),则这些现在都不会是最优决策
    如果ratio(k,j)<sumf[i]<ratio(l,k),则j优于k,l优于k
    如果ratio(l,k)<sumf[i],则j最优
    因此可以看出k是不需要的
    因此可以将k节点删去,最后得到的单调队列则有保证ratio递增

    关于单调队列具体操作:
    1.将队列中头部满足ratio(head,head+1)<sumf[i]的所有删去,因为sumf是递增的,故此后永远满足head比head+1差,所以可以删去到ratio(head,head+1)>sumf[i],此时head即最优决策。
    2.计算出新的ratio(tail,i)不断从尾部往前删除直到ratio(tail,i)>ratio(tail,tail-1).然后将i插入尾部即可

    #include<cstdio>
    #define Maxn 10005
    int i,n,s,head,tail;
    int sumt[Maxn],sumf[Maxn],ans[Maxn],p[Maxn];
    inline double rat(int x,int y){
        return((double)(ans[x]-ans[y])/(double)(sumt[x]-sumt[y]));
    }
    int main()
    {
        scanf("%d%d",&n,&s);
        for(i=1;i<=n;i++)
            scanf("%d%d",&sumt[i],&sumf[i]);
        for(i=n;i>=1;i--){
            sumt[i]+=sumt[i+1];
            sumf[i]+=sumf[i+1];
            while(head<tail&&rat(p[head],p[head+1])<=sumf[i]) head++;
            ans[i]=ans[p[head]]+sumf[i]*(s+sumt[i]-sumt[p[head]]);
            while(head<tail&&rat(p[tail-1],p[tail])>=rat(p[tail],i)) tail--;
            p[++tail]=i;
        }
        printf("%d
    ",ans[1]);
        return 0;
    }
    View Code


     Uva 14

    本质上就是裸的斜率优化dp的转型

    即求出一定条件下,图像上任意两点斜率的最大值。

    #include <cstdio>
    #include <cmath>
    #define eps 1e-13
    int ansl,ansr,n,l,d[100005],sum[100005];
    double ans;
    bool equ(double x,double y){
    return(fabs(x-y)<eps);
    }
    inline double ratio(int a,int b){
    return((sum[a]-sum[b])*1.0/(a-b));
    }
    int main()
    {
    int t;
    scanf("%d",&t);
    while(t--){
    sum[0]=0;
    scanf("%d%d",&n,&l);
    for(int i=1; i<=n; i++){
    scanf("%1d",&sum[i]);
    sum[i]+=sum[i-1];
    }
    ans=0;
    ansl=0;ansr=l;//注意初始化,可能出现0000000
    int g=1,h=0;
    for(int i=l; i<=n; i++){
    while(h>g&&ratio(d[h],i-l)<=ratio(d[h-1],d[h]))
    h--;
    d[++h]=i-l;
    while(g<h&&(ratio(d[g],i)<=ratio(d[g+1],i)))
    g++;
    if(ratio(d[g],i)>ans&&!equ(ans,ratio(d[g],i)))
    {
    ans=ratio(d[g],i);
    ansl=d[g]+1;
    ansr=i;
    }
    else if(equ(ans,ratio(d[g],i))&&i-d[g]<ansr-ansl+1){
    ansl=d[g]+1;
    ansr=i;
    }
    }
    printf("%d %d
    ",ansl,ansr);
    }
    return 0;
    }
    View Code
    
    
  • 相关阅读:
    strutr2运行流程
    ConcurrentHashMap原理分析
    面试题集锦
    jvm如何知道那些对象需要回收
    java中volatile关键字的含义
    关于Java类加载双亲委派机制的思考(附一道面试题)
    new关键字和newInstance()方法的区别
    Java中创建对象的5种方式 &&new关键字和newInstance()方法的区别
    字符串中第一个只出现一次的字符
    二进制数中1的个数
  • 原文地址:https://www.cnblogs.com/Mathics/p/3681175.html
Copyright © 2011-2022 走看看