zoukankan      html  css  js  c++  java
  • 斜率优化dp(玩具装箱)

    斜率优化dp

    斜率优化dp的思想是数形结合,将各种决策点反映在平面直角坐标系中,然后通过斜率进行优化

    做法
    首先将这道题的$n^2$的dp 算法写出来
    然后将其暴力展开
    如:f(i)=min(f(j)+(s[i]-s[j]+i-j-1-l)^2)
    令s[i]=s[i]+i,l=l+1
    原式变为$f(i)=min(f(j)+(s[i]-s[j]-l)^2)
    暴力展开后是长这样的:f(i)=f(j)+s[i]^2+s[j]^2+l^2-2s[i]s[j]-2s[i]l+2s[j]l
    我们可以将这个式子的右边转化为这种形式:只和j有关 和i,j有关 只和i有关 以及常数项
    f(i)=f(j)+s[j]^2+2s[j]l+2s[i]s[j]+s[i]^2-2s[i][l]+l^2
    现在我们可以玄学地令:
    b=f[i]
    y=f(j)+s[j]^2+2ls[j]
    k=-2s[i]
    x=s[j]
    这样这个式子就转换为了:
    b=y-k*x+一堆常数项$
    因为当循环到i的时候 s[i]的值是固定的 所以s[i]也可以看做是定值
    然后你会发现(常数项不用管,反正是常数嘛对不对,不会变的)
    y=kx+b 撒花!(但不是结尾撒花)
    这时候就可以用我们可爱的斜率优化了
    斜率优化的注释在代码里面 因为没有图所以只能可怜的在代码里面打注释了(玩具装箱)

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #define ll long long 
    #define y(T) (f[T]+s[T]*s[T]+2*s[T])
    #define x(T) (s[T])
    #define k(T) (2*s[T]-2*l)
    using namespace std;
    const int maxn=50000;
    ll n,l;
    ll cost[maxn],s[maxn],head,tail,f[maxn];
    ll q[maxn];
    inline double K(ll cxk,ll nmxl)
    {
    return 1.0*(y(nmxl)-y(cxk))/(x(nmxl)-x(cxk));
    }
    
    signed main()
    {
    scanf("%lld %lld",&n,&l);
    for(register ll i=1;i<=n;i++)
    {
    scanf("%lld",&cost[i]);
    s[i]=s[i-1]+cost[i];
    }
    for(register ll i=0;i<=n;i++)
    s[i]+=i;
    head=1;tail=1;//初始化都为1 表示严格包含head和tail区间这样可以表示已经有一个点0在队列里面了 而斜率优化dp必须在一开始的时候压一个点0进去才能保证第一段选出来
    for(register ll i=1;i<=n;i++)
    {
    while(head<tail && K(q[head],q[head+1])<k(i))    //注意此处head<tail因为必须有两个元素
    head++;//维护头,显然q[head+1]不是最优解(以后也不会是)弹掉
    f[i]=f[q[head]]+(s[i]-s[q[head]]-1-l)*(s[i]-s[q[head]]-1-l);
    while(head<tail && K(q[tail],i)<=K(q[tail-1],q[tail]))    
    tail--; //遇到凹包 需要将再队列中的点斜率大于新加入的点全部弹掉
    q[++tail]=i;
    }
    printf("%lld",f[n]);
    return 0;
    } 

    emm总而言之斜率优化的难点就在于把$n^2$算法的dp写出来之后要进行归纳,神奇的令,以及对队列tail的维护与遇到凹包的时候如何处理
    最毒瘤的是初始化以及边界条件 head=1 tail=1 head<tail
    然后就没什么了

  • 相关阅读:
    保障系统的稳定性
    Ubuntu 16.04开启SSH服务
    Linux中tty是什么(tty1~7)
    Linux显示使用命令who(转)
    Linux去重命令uniq(转)
    Linux文字分段裁剪命令cut(转)
    Linux排序命令sort(转)
    Linux查找字符串命令grep(转)
    Linux文件查找命令find(转)
    Ubuntu查看和写入系统日志
  • 原文地址:https://www.cnblogs.com/mendessy/p/11650753.html
Copyright © 2011-2022 走看看