• hdu 3507 斜率dp


    不好理解,先多做几个再看

    此题是很基础的斜率DP的入门题。

    题意很清楚,就是输出序列a[n],每连续输出的费用是连续输出的数字和的平方加上常数M

    让我们求这个费用的最小值。

    设dp[i]表示输出前i个的最小费用,那么有如下的DP方程:

    dp[i]= min{ dp[j]+(sum[i]-sum[j])^2 +M }  0<j<i

    其中 sum[i]表示数字的前i项和。

    相信都能理解上面的方程。

    直接求解上面的方程的话复杂度是O(n^2)

    对于500000的规模显然是超时的。下面讲解下如何用斜率优化DP使得复杂度降低一维。

    我们首先假设在算 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   而且 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 是可以淘汰的

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

    通过一个队列来维护。

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

    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])。

     1 /*
     2 HDU 3507
     3 
     4 */
     5 
     6 #include<stdio.h>
     7 #include<iostream>
     8 #include<string.h>
     9 #include<queue>
    10 using namespace std;
    11 const int MAXN=500010;
    12 
    13 int dp[MAXN];
    14 int q[MAXN];//队列
    15 int sum[MAXN];
    16 
    17 int head,tail,n,m;
    18 // dp[i]= min{ dp[j]+M+(sum[i]-sum[j])^2 };
    19 int getDP(int i,int j)
    20 {
    21     return dp[j]+m+(sum[i]-sum[j])*(sum[i]-sum[j]);
    22 }
    23 
    24 int getUP(int j,int k) //yj-yk部分
    25 {
    26     return dp[j]+sum[j]*sum[j]-(dp[k]+sum[k]*sum[k]);
    27 }
    28 int getDOWN(int j,int  k)
    29 {
    30     return 2*(sum[j]-sum[k]);
    31 }
    32 
    33 int main()
    34 {
    35   //  freopen("in.txt","r",stdin);
    36   //  freopen("out.txt","w",stdout);
    37     while(scanf("%d%d",&n,&m)==2)
    38     {
    39         for(int i=1;i<=n;i++)
    40            scanf("%d",&sum[i]);
    41         sum[0]=dp[0]=0;
    42         for(int i=1;i<=n;i++)
    43            sum[i]+=sum[i-1];
    44         head=tail=0;
    45         q[tail++]=0;
    46         for(int i=1;i<=n;i++)
    47         {
    48             //把斜率转成相乘,注意顺序,否则不等号方向会改变的
    49             while(head+1<tail &&  getUP(q[head+1],q[head])<=sum[i]*getDOWN(q[head+1],q[head]))
    50                head++;
    51             dp[i]=getDP(i,q[head]);
    52             while(head+1<tail && getUP(i,q[tail-1])*getDOWN(q[tail-1],q[tail-2])<=getUP(q[tail-1],q[tail-2])*getDOWN(i,q[tail-1]))
    53                     tail--;
    54             q[tail++]=i;
    55         }
    56         printf("%d
    ",dp[n]);
    57     }
    58     return 0;
    59 }
  • 相关阅读:
    类的几个内置函数,装饰器
    面向对象,类的介绍
    模块,包,获取命令行的输入
    三元运算 ,列表推导式,迭代器,生成器
    测试运行kafka的时候缺少包的错误
    mysql5.5版本以后插入中午显示问号的解决办法
    mysql 不能插入中文和显示中文
    WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
    Hive环境的安装部署(完美安装)(集群内或集群外都适用)(含卸载自带mysql安装指定版本)
    hive的安装,一般不容易察觉的hdfs的配置问题导致hive安装的失败
  • 原文地址:https://www.cnblogs.com/cnblogs321114287/p/4319981.html
Copyright © 2020-2023  润新知