• hdu 3507(DP+斜率优化)


    Print Article

    Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)
    Total Submission(s): 6653    Accepted Submission(s): 2054


    Problem Description
    Zero has an old printer that doesn't work well sometimes. As it is antique, he still like to use it to print articles. But it is too old to work for a long time and it will certainly wear and tear, so Zero use a cost to evaluate this degree.
    One day Zero want to print an article which has N words, and each word i has a cost Ci to be printed. Also, Zero know that print k words in one line will cost

    M is a const number.
    Now Zero want to know the minimum cost in order to arrange the article perfectly.
     
    Input
    There are many test cases. For each test case, There are two numbers N and M in the first line (0 ≤ n ≤ 500000, 0 ≤ M ≤ 1000). Then, there are N numbers in the next 2 to N + 1 lines. Input are terminated by EOF.
     
    Output
    A single number, meaning the mininum cost to print the article.
     
    Sample Input
    5 5 5 9 5 7 5
     
    Sample Output
    230
     
    Author
    Xnozero
     
    Source
     
    Recommend
    zhengfeng   |   We have carefully selected several similar problems for you:  3501 3504 3505 3498 3503 
    此题是很基础的斜率优化DP
     题目描述: 给你一些字,你可以选择每次打印一个或者几个字,每次打印的费用是(ci+c(i+1)+c(i+2)+....)^2+m;
    c[i]代表每个字的费用;
    设dp[i]代表打印前i个字所需的最少费用,dp[i]=min(dp[j]+(sum[i]-sum[j])^2,dp[i]);
    由于n的范围是500000,那么25*10^10,肯定会超时;
    可以采用斜率优化:

    我们首先假设在算 dp[i]时,k<j ,j点比k点优。

    也就是

    dp[j]+(sum[i]-sum[j])^2+M <= dp[k]+(sum[i]-sum[k])^2+M;

    所谓j比k优就是DP方程里面的值更小

    对上述方程进行整理很容易得到:

    [(dp[j]+sum[j]*sum[j])-(dp[k]+sum[k]*sum[k])] / 2(sum[j]-sum[k]) <=sum[i].

    注意整理中要考虑下正负,涉及到不等号的方向。

    左边我们发现如果令:yj=dp[j]+sum[j]*sum[j]   xj=2*sum[j]

    那么就变成了斜率表达式:(yj-yk)/(xj-xk) <= sum[i];

    而且不等式右边是递增的。

    所以我们可以看出以下两点:我们令g[k,j]=(yj-yk)/(xj-xk)

    第一:如果上面的不等式成立,那就说j比k优,而且随着i的增大上述不等式一定是成立的,也就是对i以后算DP值时,j都比k优。那么k就是可以淘汰的。

    如果不成立,那就说明k比j优,但是随着i的增加sum[i]增加,j始终会替换掉k.

    第二:如果 k<j<i   而且 g[k,j]>g[j,i] 那么 j 是可以淘汰的。

    假设  g[j,i]<sum[i]就是i比j优,那么j没有存在的价值

    相反如果 g[j,i]>sum[i] 那么同样有 g[k,j]>sum[i]  那么 k比 j优 那么  j 是可以淘汰

    所以这样相当于在维护一个下凸的图形,斜率在逐渐增大。

    通过一个队列来维护。 

    初始化队列:head=0,tail=0;que[tail++]=0;表示tail处不存决策。

    于是对于这题我们对于斜率优化做法可以总结如下:

    1,用一个单调队列来维护解集。

    2,假设队列中从头到尾已经有元素a b c。那么当d要入队的时候,我们维护队列的上凸性质,即如果g[d,c]<g[c,b],那么就将c点删除。直到找到g[d,x]>=g[x,y]为止,并将d点加入在该位置中。

    3,求解时候,从队头开始,如果已有元素a b c,当i点要求解时,如果g[b,a]<sum[i],那么说明b点比a点更优,a点可以排除,于是a出队。最后dp[i]=getDp(q[head]);

    #include <iostream>
    #include <cstdio>
    #include <string.h>
    #include <math.h>
    #define maxn  500010
    #define  LL int
    using namespace std;
    
    LL  sum[maxn],dp[maxn];
    int que[maxn];
    int head,tail;
    int n;
    LL  m;
    int getdp(int i,int j)
    {
        return dp[j]+m+(sum[i]-sum[j])*(sum[i]-sum[j]);
    }
    
    int getup(int j,int k) //yj-yk部分
    {
        return dp[j]+sum[j]*sum[j]-(dp[k]+sum[k]*sum[k]);
    }
    int getdown(int j,int  k)
    {
        return 2*(sum[j]-sum[k]);
    }
    
    void  solve()
    {
        head=0;
        tail=0;
        que[tail++]=0;  //队列里存储的是决策   //tail处不存决策
        for(int i=1;i<=n;i++)
        {
            //从头开始找当前状态的最优决策,g[que[head+1],que[head]] < sum[i],说明que[head+1]比que[head]更优,删除que[head]
           while(head+1 < tail && getup(que[head+1],que[head]) <= getdown(que[head+1],que[head]) * sum[i] )
                head++;   //注意写成相乘,不然要考虑除数是否为负数
            dp[i]=getdp(i,que[head]);
    
        //从尾往前,加入当前状态,如果g[i,que[tail]] < g[que[tail],que[tail-1]] ,可以排除que[tail]
           /* while(head+1 <tail && getup(i,que[tail-1]) * getdown(que[tail-1],que[tail-2]) <= getup(que[tail-1],que[tail-2])*getdown(i,que[tail-1])))
            {
               tail--;  //看到为什么RE了吗?笨蛋,括号打错了
            }*/
             while(head+1<tail && getup(i,que[tail-1])*getdown(que[tail-1],que[tail-2])<=getup(que[tail-1],que[tail-2])*getdown(i,que[tail-1]))
                        tail--;
            que[tail++]=i;
        }
        /*for(int i=1;i<=n;i++)
        printf("%.0lf ",dp[i]);*/
        printf("%d
    ",dp[n]);
    }
    int main()
    {
    
        while(scanf("%d%d",&n,&m)==2)
        {
       // init();
        sum[0]=0;
        for(int i=1;i<=n;i++)
        {
        scanf("%d",&sum[i]);
        sum[i]+=sum[i-1];
        }
    //for(int i=1;i<=n;i++)
    // printf("%lf ",sum[i]);
        solve();
        }
    return 0;
    }
  • 相关阅读:
    十招教你从屌丝变身高富帅【转】
    无法在证书存储区中找到清单签名证书”错误的解决方法【转】
    上传头像进行裁剪——jQuery + HttpHandler 实现图片裁剪(适用于论坛, SNS)【转】
    在类库中获得Session值
    WinForm中Combobox绑定值问题
    Global.cs中自动获取未处理的异常
    程序员的7个坏习惯【转】
    外部JS文件中获取ASPX页面上服务器控件ClientID
    如何使用微软企业库
    防止代码腐烂【转】
  • 原文地址:https://www.cnblogs.com/xianbin7/p/4510657.html
Copyright © 2020-2023  润新知