洛谷1295:书架(堆优化dp)

2 篇文章 0 订阅

noip前最后一篇博客了…
题面
题意:给出n个数,分成若干段,每段的和不能超过m,每段的代价为该段的最大值。问最小代价。

一个显然的dp,用f[i]表示前i个数,最后一段以i结尾的最小代价。
f[i]=min(f[j]+max(a[j+1,i])) ,其中sum[i]-sum[j]≤m。

正如小姐姐的美滋滋程度... f是非严格递增的,故只有对于a[j]为a[j]~a[i]的最大值的j,f[j]才有可能更新f[i]。
对于i,发现所有可能更新i的状态f[j],a[j]是非严格单调下降的。我们用个队列维护这个单调的a,把所有可能的j扔进一个机巧的堆里。
每次先维护单调性,再算答案。

#include <iostream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>

using namespace std;
#define mmst(a, b) memset(a, b, sizeof(a))
#define mmcp(a, b) memcpy(a, b, sizeof(b))

typedef long long LL;

const int N=100100,oo=1e9+7;

int n,m,f[N],a[N],sum[N];
int q[N],hh=1,tt=1;

struct yy
{
    int h[N],cnt;
    void add(int x)
    {
        h[++cnt]=x;
        int now=cnt;
        while(now>1&&h[now>>1]>h[now])
        swap(h[now],h[now>>1]),now>>=1;
    }

    void down()
    {
        h[1]=h[cnt];
        cnt--;
        int now=1;
        while((now<<1)<=cnt)
        {
            int son=now<<1|1;
            if(son>cnt||h[now<<1]<h[son])
            son=now<<1;
            if(h[son]<h[now])
            swap(h[son],h[now]),now=son;
            else
            break;
        }
    }
}A,B;

int small()
{
    while(A.h[1]==B.h[1]&&B.cnt)
    {
        A.down();
        B.down();
    }
    return A.h[1];
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
    a[0]=oo;

    A.add(oo);
    int now=0;
    for(int i=1;i<=n;i++)
    {
        f[i]=oo;
        while(sum[i]-sum[now]>m)
        {
            if(q[hh]==now)
            {
                if(hh<tt)
                B.add(f[now]+a[q[hh+1]]);
                hh++;
            }
            now++;
        }

        while(hh<=tt&&a[i]>a[q[tt]])
        {
            if(hh<tt)
            B.add(f[q[tt-1]]+a[q[tt]]);
            tt--;
        }

        q[++tt]=i;
        if(hh<tt)
        A.add(f[q[tt-1]]+a[i]);

        if(now==q[hh])
        f[i]=a[q[hh+1]]+f[now];
        else
        f[i]=a[q[hh]]+f[now];

        if(hh<tt)
        f[i]=min(f[i],small());
    }

    cout<<f[n]<<endl;

    return 0;
}

这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值