hdu-3507 Print Article

31 篇文章 0 订阅
12 篇文章 0 订阅

题意:
给出n,m,求在n个数中分成任意段;
每段的花销是(sigma(a[l],a[r])+m)^2;
求这个最小值;


题解:
斜率优化入门题;
然而入门并不是这么容易的(笑);
我们可以很容易的得到dp方程;
f[i]=f[j]+(s[i]-s[j]+m)^2
其中s[x]表示x的前缀和,0<=j<i;
O(n^2)显然超时;
对于dp方程我们把平方展开可得;
f[i] = f[j]+(s[i]+m)^2+s[j]^2-2(s[i]+m)s[j];
于是对于两个决策j,k来说 (设k<j)
倘若j优于k,那么f[j]+(s[i]+m)^2+s[j]^2-2(s[i]+m)s[j] < f[k]+(s[i]+m)^2+s[k]^2-2(s[i]+m)s[k];
化简 f[j]+s[j]^2-f[k]-s[k]^2 < 2(s[i]+m)(s[j]-s[k])
(f[j]+s[j]^2-f[k]-s[k]^2)/(s[j]-s[k]) < 2(s[i]+m)
发现无论对于任意的i来说,左面都是常量;
而右面则是关于i递增的;
那么我们就可以确定,当j,k满足以上条件时,再对于后面的i来说,j也总优于k,所有可以将k删掉;
而反之则不行;

为了方便表述,设f[x]+s[x]^2=g[x];
那么上式就为(g[j]-g[k])/(s[j]-s[k])
与斜率的表述形式相似吧;
我们需要在O(1)的时间复杂度内找到最优的决策,所以我们可以考虑维护队列优化dp;
由上面的结论我们可以一一把队首干掉而使队首最优;
但这种想法有反例存在,就是在三个决策,k<j<i;
当i与k都比j优时,我们无法得知i与k孰优孰劣;
所以我们需要对此作出判断,讨论jk斜率大于ij斜率的时候;
如果2s[i]大于两个斜率,那么应为i最优,jk可以删掉;
如果2s[i]大于ij斜率而小于jk斜率,那么就是i优于j而且k优于j;
因为i已经比j优,所以j可以删掉;
如果2s[i]小于两个斜率,那么当前最优为k,并且我们无法删掉点;
但是如果讨论后面s[i]继续增大的情况,我们发现与前两个相同;
j就不可能为最优值,所以我们在这里依然可以删掉j;
那么jk斜率大于ij斜率的情况不可能出现了,因为那时j会被删掉;
因此斜率是单调递增的,即图像为下凹曲线;(所以某题解说的是错的啦(笑))

实现的时候就是对于每个需要更新的i;
I:取队首,判断其是否比第二项优,不优就删掉,直到优为止;
II:这时队首就是最优的决策,用其更新i;
III:将i加入队列之前,我们要维护斜率单调性;
IV:把i加进去;


代码:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 500005
using namespace std;
typedef long long ll;
ll a[N],s[N],f[N],q[N];
int main()
{
    int n,i,j,k,st,en;
    ll m;
    while(scanf("%d%I64d",&n,&m)!=EOF)
    {
        for(i=1;i<=n;i++)
        {
            scanf("%I64d",a+i);
            s[i]=s[i-1]+a[i];
        }
        q[st=en=1]=0;
        for(i=1;i<=n;i++)
        {
            while(st<en&&(f[q[st]]-f[q[st+1]]+s[q[st]]*s[q[st]]-s[q[st+1]]*s[q[st+1]])>=2*s[i]*(s[q[st]]-s[q[st+1]]))
            {
                st++;
            }
            f[i]=f[q[st]]+(s[i]-s[q[st]])*(s[i]-s[q[st]])+m;
            while(st<en&&(f[q[en]]-f[q[en-1]]+s[q[en]]*s[q[en]]-s[q[en-1]]*s[q[en-1]])*(s[i]-s[q[en]])>=(f[i]-f[q[en]]+s[i]*s[i]-s[q[en]]*s[q[en]])*(s[q[en]]-s[q[en-1]]))
            {
                en--;
            }
            q[++en]=i;
        }
        printf("%I64d\n",f[n]);
    }
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值