• 斜率优化dp练习


    1.HDU3507

    裸题,有助于理解斜率优化的精髓。

    dp[i]=min(dp[j]+m+(sum[i]-sum[j])2)

    很显然不是单调队列。

    根据斜率优化的的定义,就是先设两个决策j,k

    什么时候我们认为在 i 的环境下 j 比 k 好呢?根据上面的递推式,得到下面这么一个式子

    dp[j]+m+(sum[i]-sum[j])2<dp[k]+m+(sum[i]-sum[k])2

    打开括号:

    dp[j]+m+sum[i]2+sum[j]2-2*sum[i]*sum[j]<dp[k]+m+sum[i]2+sum[k]2-2*sum[i]*sum[k]

    移项,将有 i 的项移到右侧:

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

    除下来:

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

    好了这就是斜率了^_^

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 typedef long long lnt;
     5 int Q[1000000];
     6 lnt sum[1000000];
     7 lnt dp[1000000];
     8 int h,t;
     9 lnt n;
    10 lnt m;
    11 lnt X(int x)
    12 {
    13     return dp[x]+sum[x]*sum[x];
    14 }
    15 lnt tp(int i,int j)
    16 {
    17     return X(i)-X(j);
    18 }
    19 lnt btm(int i,int j)
    20 {
    21     return 2*sum[i]-2*sum[j];
    22 }
    23 int main()
    24 {
    25     while(scanf("%lld%lld",&n,&m)!=EOF)
    26     {
    27         sum[0]=0;
    28         for(int i=1;i<=n;i++)
    29             scanf("%lld",&sum[i]);
    30         for(int i=1;i<=n;i++)
    31             sum[i]+=sum[i-1];
    32         h=t=1;
    33         Q[1]=0;
    34         dp[0]=0;
    35         for(int i=1;i<=n;i++)
    36         {
    37             while(h<t&&(tp(Q[h+1],Q[h])<=sum[i]*btm(Q[h+1],Q[h])))
    38                 h++;
    39             dp[i]=dp[Q[h]]+m+(sum[i]-sum[Q[h]])*(sum[i]-sum[Q[h]]);
    40             while(h<t&&(tp(Q[t],Q[t-1])*btm(i,Q[t])>=tp(i,Q[t])*btm(Q[t],Q[t-1])))
    41                 t--;
    42             Q[++t]=i;
    43         }
    44         printf("%lld
    ",dp[n]);
    45     }
    46     return 0;
    47 }

     2.BZOJ1010: [HNOI2008]玩具装箱toy

    这道题依然斜率单调

    dp方程自己推:

    dp[i]=min(dp[j]+(i-j-1+sum[i]-sum[j]-L)2)

    依然假设在 i 的环境下决策 j 优于 k 

    那么:

    dp[j]+(i-j-1+sum[i]-sum[j]-L)2<dp[k]+(i-k-1+sum[i]-sum[k]-L)2

    将常数项与与 i 有关的项放到一起,展开:

    (sum[j]+j)2-2*(i+sum[i]-L-1)*(sum[j]+j)+dp[j]<(sum[k]+k)2-2*(i+sum[i]-L-1)*(sum[k]+k)+dp[k]

    设函数 f(x)=sum[x]+x , h(x)=x+sum[x]-L-1 , g(x)=dp[x]+f(x)2

    得到当:

    g(j)-g(k)<2*h(i)*(f(j)-f(k)) 时,j 比 k 优秀。

    f(x)单调递增,斜率单调。

    时间复杂度O(n)

    代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 typedef long long lnt;
     5 lnt sum[1000000];
     6 lnt dp[1000000];
     7 int Q[1000000];
     8 int h,t;
     9 int n;
    10 lnt L;
    11 lnt f(int x)
    12 {
    13     return (sum[x]+(lnt)(x));
    14 }
    15 lnt k(int x)
    16 {
    17     return ((lnt)(x)+sum[x]-L-1);
    18 }
    19 lnt g(int x)
    20 {
    21     return (dp[x]+f(x)*f(x));
    22 }
    23 lnt squ(lnt x)
    24 {
    25     return x*x;
    26 }
    27 int main()
    28 {
    29     scanf("%d%lld",&n,&L);
    30     for(int i=1;i<=n;i++)
    31     {
    32         scanf("%lld",&sum[i]);
    33         sum[i]+=sum[i-1];
    34     }
    35     Q[1]=0;
    36     dp[0]=0;
    37     h=t=1;
    38     for(int i=1;i<=n;i++)
    39     {
    40         while(h<t&&g(Q[h+1])-g(Q[h])<2ll*k(i)*(f(Q[h+1])-f(Q[h])))
    41             h++;
    42         dp[i]=dp[Q[h]]+squ((lnt)(i-Q[h]-1)+sum[i]-sum[Q[h]]-L);
    43         while(h<t&&(((g(Q[t-1])-g(Q[t]))*(f(Q[t])-f(i)))>((g(Q[t])-g(i))*(f(Q[t-1])-f(Q[t])))))
    44             t--;
    45         Q[++t]=i;
    46     }
    47     printf("%lld
    ",dp[n]);
    48     return 0;
    49 }

     3.BZOJ1911: [Apio2010]特别行动队

    斜率单调。

    方程自己推:

    设:g(x)=a*x2+b*x+c

    dp[i]=max(dp[j]+g(sum[i]-sum[j]))

    设在 i 环境下决策 j 优于 k

    dp[j]+g(sum[i]-sum[j])>dp[k]+g(sum[i]-sum[k])

    设f(x)=dp[x]+a*sum[x]2-b*sum[x]

    则当:

    f(j)-f(k)>2*a*sum[i]*(sum[j]-sum[k])

    设 j > k 斜率单调

    时间复杂度O(n)

    代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cstdio>
     4 typedef long long lnt;
     5 lnt sum[1000006];
     6 lnt dp[1000006];
     7 int Q[1000006];
     8 int h,t;
     9 int n;
    10 lnt a,b,c;
    11 lnt f(int x)
    12 {
    13     return dp[x]+a*sum[x]*sum[x]-b*sum[x];
    14 }
    15 lnt g(lnt x)
    16 {
    17     return a*x*x+b*x+c;
    18 }
    19 int main(void)
    20 {
    21     sum[0]=0;
    22     h=t=1;
    23     scanf("%d",&n);
    24     scanf("%lld%lld%lld",&a,&b,&c);
    25     for(int i=1;i<=n;i++)
    26     {
    27         scanf("%lld",&sum[i]);
    28         sum[i]+=sum[i-1];
    29     }
    30     Q[1]=0;
    31     dp[0]=0;
    32     for(int i=1;i<=n;i++)
    33     {
    34         while(h<t&&(f(Q[h+1])-f(Q[h]))>2*a*sum[i]*(sum[Q[h+1]]-sum[Q[h]]))
    35             h++;
    36         dp[i]=dp[Q[h]]+g(sum[i]-sum[Q[h]]);
    37         while(h<t&&(f(Q[t])-f(Q[t-1]))*(sum[i]-sum[Q[t]])<=(f(i)-f(Q[t]))*(sum[Q[t]]-sum[Q[t-1]]))
    38             t--;
    39         Q[++t]=i;
    40     }
    41     printf("%lld
    ",dp[n]);
    42     return 0;
    43 }

     4.BZOJ4518: [Sdoi2016]征途

    这次变成二维的了。

    都一样,展开方程+斜率优化。

    这次要对于每一天进行O(n)转移,共m天,时间复杂度O(n*m)

    代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 typedef long long lnt;
     5 lnt dp[4000][4000];
     6 lnt sum[10000];
     7 int Q[1000000];
     8 lnt n,m;
     9 lnt L;
    10 int h,t;
    11 lnt squ(lnt x)
    12 {
    13     return x*x;
    14 }
    15 lnt f(int x,int i)
    16 {
    17     return dp[x][i]+m*squ(sum[x])+2ll*L*sum[x];
    18 }
    19 int main()
    20 {
    21     scanf("%lld%lld",&n,&m);
    22     for(int i=1;i<=n;i++)
    23     {
    24         scanf("%lld",&sum[i]);
    25         sum[i]+=sum[i-1];
    26     }
    27     L=sum[n];
    28     for(int i=1;i<=n;i++)
    29     {
    30         dp[i][1]=m*squ(sum[i])-2ll*L*sum[i];
    31     }
    32     for(int d=2;d<=m;d++)
    33     {
    34         t=h=1;
    35         Q[1]=0;
    36         for(int i=1;i<=n;i++)
    37         {
    38             while(h<t&&(f(Q[h+1],d-1)-f(Q[h],d-1))<2ll*m*sum[i]*(sum[Q[h+1]]-sum[Q[h]]))
    39                 h++;
    40             dp[i][d]=dp[Q[h]][d-1]+m*squ(sum[i]-sum[Q[h]])-2ll*L*(sum[i]-sum[Q[h]]);
    41             while(h<t&&(f(Q[t],d-1)-f(Q[t-1],d-1))*(sum[i]-sum[Q[t]])>(f(i,d-1)-f(Q[t],d-1))*(sum[Q[t]]-sum[Q[t-1]]))
    42                 t--;
    43             Q[++t]=i;
    44         }
    45     }
    46     printf("%lld
    ",dp[n][m]+squ(L));
    47     return 0;
    48 }
  • 相关阅读:
    什么是Python???
    python中闭包详解
    python类中的__init__和__new__方法
    python中不需要函数重载的原因
    装饰器详解
    IDEA中导入非maven项目(从eclipse中迁移过来的)
    CLion、cmake、mingw、openCV安装
    跨域问题的一些思考
    maven中jar包手动导入
    对于序列化的理解
  • 原文地址:https://www.cnblogs.com/blog-Dr-J/p/9622843.html
Copyright © 2020-2023  润新知