• POJ 3709 斜率优化DP


    此题有是方程好写,优化很难(对于神犇们,简直太水了)的一道题

    建议做这道题之前先看这道题:http://www.lydsy.com/JudgeOnline/problem.php?id=1010

    题意:

    将一个升序的,有N个元素的序列,分组。要求每组的元素不少于K个,计算出组内各元素与最小元素的之差的和,将每组的这个值加起来,其和要最小。

    思路:

    由以上可得DP方程:

    dp[i]=MIN(dp[j]+sum[i]-sum[j]-(i-j)*arr[j+1]); j<i-k+1

     

    开始斜率优化(不考虑每组不少于K个元素):

    1.证明较优决策点对后续状态影响的持续性

      证明很简单,不证了,有兴趣的话,参考上一篇文章

     

    2.求斜率方程:一般化为左边是JK,右边是I的形式

      假设J<K,且在K点的决策比J好,则有

     dp[j]+sum[i]-sum[j]-(i-j)*arr[j+1]>= dp[k]+sum[i]-sum[k]-(i-k)*arr[k+1]

    化简得:

      dp[j]-dp[k]-sum[j]+sum[k]+j*arr[j+1]-k*arr[k+1]>=i* (arr[j+1]-arr[k+1])

     

    G(k,j)= dp[j]-dp[k]-sum[j]+sum[k]+j*arr[j+1]-k*arr[k+1]

       S(k,j)= arr[j+1]-arr[k+1]

     

    则上式化为G(k,j)>=i*S(k,j)

    G(k,j)/S(k,j)<=i 记住变号,因为S(k,j)<0

    X(k,j)= G(k,j)/S(k,j)

    所以斜率方程:X(k,j)<=i

     

    3.规定队列的维护规则

    队首维护:

      假设A,B(A<B)是队首元素,X(B,A)<=i,BA,删除A,否则不需维护.

     

    队尾维护:

        假设A,B,C(A<B<C)是队尾元素

    a.X(B,A)<=i,X(C,B)<=i,CB,BA

    b.X(B,A)<=i,X(C,B)>i,BC,BA,B为极大值

    c.X(B,A)>i,AB

     

    a,c情况直接删掉B,b情况保留.b情况可改为X(B,A)<X(C,B)

     

    好,以下考虑每组不少于K个元素这个限制。

    要解决这个限制,只需延迟加入的时机即可。

    若延迟K-1个回合加入,有可能使前一组的个数少于K个。

    若延迟2*k-1个回合加入,则不会出现这情况。但此时加入的数应是i-k+1(假设是第I回合)

    第一次做斜率优化DP,照着题解写的,以上内容转自:http://blog.sina.com.cn/s/blog_5f5353cc0100jxxo.html

    代码按照题解思路写的:

    DP真的好神奇!~

    View Code
     1 #include <cstdio>
     2 #include <cstring>
     3 #include <cstdlib>
     4 #include <iostream>
     5 
     6 #define N 505000 
     7 
     8 using namespace std;
     9 
    10 int n,k,tt;
    11 __int64 dp[N],sum[N],a[N],q[N];
    12 
    13 void read()
    14 {
    15     scanf("%d%d",&n,&k);
    16     for(int i=1;i<=n;i++)
    17     {
    18         scanf("%I64d",&a[i]);
    19         sum[i]=sum[i-1]+a[i];
    20     }
    21 }
    22 
    23 __int64 G(int y,int x)
    24 {
    25     return dp[x]-dp[y]-sum[x]+sum[y]+x*a[x+1]-y*a[y+1];
    26 }
    27 
    28 __int64 S(int y,int x)
    29 {
    30     return a[x+1]-a[y+1];
    31 }
    32 
    33 void go()
    34 {
    35     dp[0]=0;
    36     int h=1,t=1;
    37     q[t++]=0;
    38     for(int i=1,x,y,z;i<=n;i++)
    39     {
    40         while(h<t-1&&G(q[h+1],q[h])>=i*S(q[h+1],q[h])) h++;//把不可能成为最优值的出队 
    41         
    42         dp[i]=dp[q[h]]+sum[i]-sum[q[h]]-(i-q[h])*a[q[h]+1];
    43         
    44         if(i>=2*k-1) q[t++]=i-k+1;//延迟更新 
    45         
    46         for(int j=t-2;j-1>=h;j--)
    47         {
    48             x=q[j-1]; y=q[j]; z=q[j+1];
    49             if(G(y,x)*S(z,y)>=G(z,y)*S(y,x)) q[j]=q[--t];
    50             else break;
    51         }
    52     }
    53     printf("%I64d\n",dp[n]);
    54 }
    55 
    56 int main()
    57 {
    58     scanf("%d",&tt);
    59     while(tt--)
    60     {
    61         read();
    62         go();
    63     }
    64     return 0;
    65 }

     

    没有人能阻止我前进的步伐,除了我自己!
  • 相关阅读:
    Linux的五个查找命令
    Linux中ftp不能上传文件/目录的解决办法
    linux下的chmod,chown和chgrp
    Memcache基础教程
    Web服务器磁盘满故障
    linux定时任务生产java服务无法执行问题案例
    Linux 用户和用户组管理
    10-xargs 简明笔记
    51-du 显示关于目录层次结构或文件磁盘使用情况的信息
    46-df 显示磁盘空间的使用情况
  • 原文地址:https://www.cnblogs.com/proverbs/p/2713104.html
Copyright © 2020-2023  润新知