[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
  • 尝试着整理变形然后查看单调性,因为看到有人说可以把决策表打出来看看单调性,于是尝试了一下这个方法。随便学习了一下批处理的皮毛,以前写的对拍程序只能够手工一次一次对拍,现在终于能够写出一个更加自动化的程序来了
  • ExpandedBlockStart.gif 批处理
     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找毛病了

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

    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的表示形式,说实话,这样处理起来的确是更美观了一些。

转载于:https://www.cnblogs.com/perseawe/archive/2012/05/12/BZ1010.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值