1010: [HNOI2008]玩具装箱toy

18 篇文章 0 订阅
1 篇文章 0 订阅

原来的DP式子:

设f[i]为前i个玩具装箱的总费用

f[i] = min{ f[j]+ (i-(j+1)+s[i]-s[j]-l)^2 }

展开得

令g[i]=i+s[i]

h[j]=j+1+s[j]+l

f[i] = min{ f[j]+(g[i]-h[j]) ^2} = min{f[j] + h[j] ^ 2 - 2 * g[i] *h[j] } + g[i] ^ 2

设j1<j2

如果决策j2 比j1优

f[j1]+ h[j1]^ 2 - 2* g[i] *h[j1]>f[j2]+ h[j2]^ 2 - 2* g[i] *h[j2]

(h[j2]-h[j1])*2*g[i]>f[j2]+h[j2]^2-f[j1]-h[j1]^2

slope(j1,j2)<2*g[i]

g[i]单调递增

f[j]+h[j]^2单调递增

同时,如果在a,b,c中b最优,那么slope(a,b)<2*g[i]   and  slope(b,c)>2*g[i] ==>slope(b,c)>slope(a,b)斜率单调递增

用单调队列维护下凸壳(斜率是正的,即上下增减性相同)

平方要注意long long 

还有 slope(j1,j2)虽然数值上等于slope(j2,j1) 但是放在不等式中如果改变j1, j2位置,实际上不等号是要改变的,所以slope是唯一确定的

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;
#define MAXN 50010
int n,l,top,tail,j;
int q[MAXN],a[MAXN];
long long s[MAXN],f[MAXN];
long long F(int x)
{
    return f[x]+s[x]*s[x]+2*l*s[x];
}
double slope(int a,int b)
{
    return (F(a)-F(b))/(s[a]-s[b]);
}
int main()
{
    scanf("%d%d", &n, &l);
    l++;
    for (int i=1;i<=n;i++)
        scanf("%d", &a[i]);
    for (int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
    for (int i=1;i<=n;i++) s[i]+=i;
    top=1;tail=1;
    q[top]=0;
    for (int i=1;i<=n;i++)
    {
        while (top<tail && slope(q[top],q[top+1])<=2*s[i]) top++;
        j=q[top];
        f[i]=f[j]+(s[i]-s[j]-l)*(s[i]-s[j]-l);
        while (tail>top && slope(q[tail],i)<=slope(q[tail-1],q[tail])) tail--;
        q[++tail]=i;
    }
    printf("%lld", f[n]);
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值