• 解题:HNOI 2008 玩具装箱


    题面

    搞了一晚上斜率优化,大概懂了一点,写写

    原来常用的优化dp方法:做前缀和,预处理,数据结构维护

    现在有转移方程长这样的一类dp:$dp[i]=min(dp[i],k[i]*x[j]+y[j]+c[i]+a)$,其中$c[i],k[i],x[j],y[j]$都是关于$i$或者$j$的变量,在$i$或者$j$确定时不变,$a$是个常量

    然后发现$x[j]$带着一个$k[i]$的系数,不好优化

    从另一个角度考虑,想想高中老师教给我们的线性规划

    可以发现因为对于每次转移的$i$来说$c[i],k[i]$都不变,我们可以把$k[i]*x[j]+y[j]$看做是一条直线(初中的一次函数),$k[i]$是斜率,然后$x[j]$是横坐标,$y[j]$是纵坐标,别的都是关于$i$的变量或者常量,不用管。那么实际上我们求的$dp$数组的最终结果就是这条直线的截距的最值(初中的与y轴的交点)

    然后我们发现发现对于每个下标我们都可以依照上面的$x[j],y[j]$把它表示成平面上的一个点$(x,y)$,然后这些点会形成一个点集,根据线性规划的知识,可以发现根据斜率的正/负我们的最优决策点都在下/上凸包上,于是可以优化了

    当我们每次转移用到的斜率单调时,直接用单调队列维护凸包,先把斜率大/小的都踢掉,转移之后再把现在不在凸包上的点也都踢掉,最后把当前点加进去

    当我们每次转移用到的斜率不单调时,就不能根据斜率直接踢了,但仍然用单调队列维护凸包,只是把找最优决策点用二分代替

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 const int N=50005;
     6 long long len[N],dp[N],que[N],n,x,f,b;
     7 inline long long c1(int p){return len[p]+p;}
     8 inline long long c2(int p){return c1(p)+x+1;}
     9 inline long long K(int p){return 2*c1(p);}
    10 inline long long X(int p){return c2(p);}
    11 inline long long Y(int p){return c2(p)*c2(p)+dp[p];}
    12 inline long long S(int a,int b){return (double)(Y(b)-Y(a))/(double)(X(b)-X(a));}
    13 int main()
    14 {
    15     scanf("%lld%lld",&n,&x);
    16     for(int i=1;i<=n;i++)
    17         scanf("%lld",&len[i]),len[i]+=len[i-1];
    18     que[f=b=0]=0;
    19     for(int i=1;i<=n;i++)
    20     {
    21         while(b-f>=1&&S(que[f],que[f+1])<K(i)) f++;
    22         dp[i]=dp[que[f]]+(c1(i)-c2(que[f]))*(c1(i)-c2(que[f]));
    23         while(b-f>=1&&S(que[b-1],i)<S(que[b],que[b-1])) b--; que[++b]=i;
    24     }
    25     printf("%lld",dp[n]);
    26     return 0;
    27 }
    View Code
  • 相关阅读:
    清北刷题班day3 morning
    [NOI1997] 积木游戏(dp)
    [NOI1999] 棋盘分割(推式子+dp)
    2017北京国庆刷题Day7 afternoon
    湖南集训day8
    湖南集训day7
    湖南集训day6
    湖南集训day5
    湖南集训day4
    湖南集训day3
  • 原文地址:https://www.cnblogs.com/ydnhaha/p/10004299.html
Copyright © 2020-2023  润新知