[BZOJ]1010: [HNOI2008]玩具装箱toy——斜率优化动态规划——Update:2012年5月13日

[题目大意]
  • 给定N个物品,可以连续的划分为若干个组,每个组的代价是(物品数-1+每个物品单独的代价-L)^2,求最小代价

[分析题解]
  •  朴素F[I]:=Min(F[J]+(J-I-1+Sum[I]-Sum[J]-L)^2)
  • 当然会TLE
  • 尝试着整理变形然后查看单调性,因为看到有人说可以把决策表打出来看看单调性,于是尝试了一下这个方法。随便学习了一下批处理的皮毛,以前写的对拍程序只能够手工一次一次对拍,现在终于能够写出一个更加自动化的程序来了
  • 批处理
     1 :loop
     2 B1010_MakeData
     3 B1010
     4 B1010_Check
     5 fc B1010_Check.out B1010.txt /a | find "no differences" > nul && goto :loop
     6 PAUSE
     7 
     8 ——————————割————————————
     9 :loop//标号
    10 B1010_MakeData//数据生成器
    11 B1010//朴素程序
    12 B1010_Check//对拍程序
    13 fc B1010_Check.out B1010.txt /a | find "no differences" > nul && goto :loop
    14 PAUSE
    15 "|"的作用是将前一个命令的返回值作为后一个程序的输入,第5句的作用是,如果fc程序返回的信息中没有"no differences"这个字串就跳出,否则返回标号loop.
    16 "&&"的作用是,如果前面的命令为真则执行后面
  •  以后就不用盯着了,将PAUSE命令去掉之后就可以再任务栏中的图标消失后直接去查看.in找毛病了

  • 然后我就生成了很多数据,用朴素程序将每一个决策点打印了出来,又用另外的一个程序来检测是不是单调,发现真的非常单调
  • 这样尝试着能不能斜率优化
  • 别人非常条理的题解
    纠结了半天的题,不过总算完全弄明白斜率优化这思想了。

    DP方程不难想:
    DP[I]=MIN(DP[J]+(SUM[I]-SUM[J]+I-J-1-L)^2) ,J<I
    其中DP[I]表示处理到第I个玩具时最少的费用。

     

    直接来显然TLE,一维方程一般用斜率优化。


    先化简下方程:
    令F[I]=SUM[I]+I,C=1+L,则化为
    DP[I]=MIN(DP[J]+(F[I]-F[J]-C)^2)

     

     

    以下是用这方法的步骤:
    1.证明较优决策点对后续状态影响的持续性
    假设在状态I处,有两个决策点J,K(J<K),且决策K比决策J好,即
       DP[K]+(F[I]-F[K]-C)^2<=DP[J]+(F[I]-F[J]-C)^2
    则,对于状态I之后的某状态T,  F[T]=F[I]+V
    要证
    DP[K]+(F[T]-F[K]-C)^2<=DP[J]+(F[T]-F[J]-C)^2
    只需证
    DP[K]+(F[I]+V-F[K]-C)^2<=DP[J]+(F[I]+V-F[J]-C)^2
    只需证
    DP[K]+(F[I]-F[K]-C)^2+2*(F[I]-F[K]-C)*V+V^2<=DP[J]+(F[I]-F[J]-C)^2+2*(F[I]-F[J]-C)*V+V^2
    联系假设只需要证:
    2*(F[I]-F[K]-C)*V<=2*(F[I]-F[J]-C)*V
    即证:
    F[K]>=F[J]
    F[I]显然为增函数,且J<K,所以上式得证.

     

     

    2.求斜率方程:一般化为左边是J,K,右边是I的形式
    由DP[K]+(F[I]-F[K]-C)^2<=DP[J]+(F[I]-F[J]-C)^2
    展开得:
    DP[K]+F[I]^2-2*F[I]*(F[K]+C)+(F[K]+C)^2<=DP[J]+F[I]^2-2*F[I]*(F[J]+C)+(F[J]+C)^2
    即:
    DP[K]+(F[K]+C)^2-DP[J]-(F[J]+C)^2<=2*F[I]*(F[K]-F[J])

    (DP[K]+(F[K]+C)^2-DP[J]-(F[J]+C)^2)/(2*(F[K]-F[J]))<=F[I]

    G(K,J)=DP[K]+(F[K]+C)^2-DP[J]-(F[J]+C)^2
    S(K,J)=2*(F[K]-F[J])
    X(K,J)=G(K,J)/S(K,J)
    所以斜率方程为X(K,J)<=F[I]

     


    3.规定队列的维护规则
    队首维护:
        假设A,B(A<B)是队首元素,若X(B,A)<=F[I],则B比A好,删除A,否则不需维护.

    队尾维护:
        假设A,B,C(A<B<C)是队尾元素
    a.若X(B,A)<=F[I],且X(C,B)<=F[I],则C比B好,B比A好
    b.若X(B,A)<=F[I],且X(C,B)>F[I],则B比C好,B比A好,B为极大值
    c.若X(B,A)>F[I],A比B好

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

     


    AC CODE:
    //[HNOI2008]玩具装箱toy       1180K 136MS 
    //地址:http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1010

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>

    int MIN(int a,int b) { if( a<b )  return a;  else return b; }
    int MAX(int a,int b) { if( a>b )  return a;  else return b; }
    #define CLR(NAME,VALUE) memset(NAME,VALUE,sizeof(NAME))

    using namespace std;

    typedef __int64 LL;

    const int N=50000+10;

    LL f[N];
    LL dp[N];

    int q[N];


    LL G(int k,int j,int& c) { //在化简公式不小心把C的符号弄错了,调试了很久才发现...
     return dp[k]+(f[k]+c)*(f[k]+c)-dp[j]-(f[j]+c)*(f[j]+c);
    }

     

     

    LL S(int k,int j) {
     return 2*(f[k]-f[j]);
    }


    int main() {
      int n,l,c,i,j,x,y,z,tmp;
      while( scanf("%d%d",&n,&l)!=EOF ) {
       f[0]=0;
       for(i=1;i<=n;++i) {
        scanf("%d",&tmp);
        f[i]=f[i-1]+tmp;
       
    }

       for(i=1;i<=n;++i) {
        f[i]+=i;
       
    }

       c=1+l;
       dp[0]=0;
       int head=0,tail=0;
       q[tail++]=0;
       for(i=1;i<=n;++i) {
        while( head<tail-1 && G(q[head+1],q[head],c)<=f[i]*S(q[head+1],q[head]) ) {
         ++head;
        
    }

        x=q[head];
        dp[i]=dp[x]+(f[i]-f[x]-c)*(f[i]-f[x]-c);

                 q[tail++]=i;
        for(j=tail-2;j>head;--j) {
         z=q[j+1];
         y=q[j];
         x=q[j-1];

         if( !(G(y,x,c)*S(z,y)<G(z,y,c)*S(y,x)) ) {
           q[j]=q[--tail];
         
    }
         else {
           break;
         
    }
        }
       }

       printf("%I64d\n",dp[n]);
      }
       
         return 0;
    }
  • 斜率优化基本都是这么处理的
  • 先备个案吧,回头再多复习复习
  • 然后这个地方有各种各样的动归,也备个案,回头一并A掉

 [另外]

 Usaco练习组中有一道题目叫做fc,是一道关于凸包的问题,前一段时间我重新写了一遍,使用的文件输入,屏幕输出。今天在使用批处理的时候,发现fc命令不断的返回很多的数值,像是坐标一样,我找了很多的地方,都没有找到错误。最后才恍然大悟,是fc这个程序的问题。批处理在调用的时候,并没有调用Dos中的fc,而是在目录下调用了fc这个凸包程序。又因为使用了文件输入,屏幕输出,所以直接输出了凸包的坐标而没有提示要求输入。

有时候一个程序中某个子程序有问题的时候,不要只盯着这一个子程序看,问题也有可能出在其他地方。——调试程序须知

 


[Update at 2012-5-13]

写了一下这个题目,但是对队尾的维护做了修改,没有使用三点测试,而是直接测试队尾元素与这次计算出的I元素对于I+1的优劣性。毫无疑问的WA了。于是考虑下为什么,说实话这是我第一次接触这种G/S的表示形式,以前我都是设一个(X,Y),将一个决策点表示为一个坐标系里的点的,然后根据单调性维护凸包(《用单调性优化动态规划》)。但是我考虑了很久很久之后,明白了其中的原因,这种G/S本质上是于(X,Y)模式是一样的,其对队尾三个点的操作等价于维护凸包。想明白了这个我就彻底明白了这种G/S的表示形式,说实话,这样处理起来的确是更美观了一些。

posted @ 2012-05-12 20:18  PerSeAwe  阅读(1680)  评论(1编辑  收藏  举报