[BZOJ1233][Usaco2009Open]干草堆tower(dp+决策单调性)

245 篇文章 0 订阅

题目描述

传送门

题解

O(n3) 的dp随便搞搞
g(i)表示将i~n都用完的最后一层的最小宽度
f(i)表示将i~n都用完的最大高度
可以发现f和g的决策是相同的,也就是说,使最后一层宽度最小也就同时使高度最大了
O(n2) 的转移是只要找到第一个满足的就直接转移(因为前缀和一定是不降的)
然后很显然这个决策是有单调性的(i->i-1)
于是可以在每一次转移之后二分一下这个点可以转移到的有哪些点
又因为是n..1转移,所以拿一个数据结构直接覆盖就好了

代码

O(n3)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 1005

int n,ans;
int w[N],s[N],f[N][N];

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;++i) scanf("%d",&w[i]),s[i]=s[i-1]+w[i];
    for (int i=1;i<=n;++i)
        for (int j=1;j<=i;++j)
        {
            for (int k=1;k<j;++k)
                if (s[i]-s[j-1]<=s[j-1]-s[k-1])
                    f[i][j]=max(f[i][j],f[j-1][k]);
            ++f[i][j];
        }
    for (int i=1;i<=n;++i)
        ans=max(ans,f[n][i]);
    printf("%d\n",ans);
}

O(n2)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 100005

int n,w[N],s[N],f[N],g[N],d[N];

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;++i) scanf("%d",&w[i]),s[i]=s[i-1]+w[i];
    for (int i=n;i>=1;--i)
        for (int j=i+1;j<=n+1;++j)
            if (g[j]<=s[j-1]-s[i-1])
            {
                g[i]=s[j-1]-s[i-1];
                f[i]=f[j]+1;
                d[i]=j;
                break;
            }
    printf("%d\n",f[1]);
}

O(nlogn)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 100005

int n,w[N],s[N],f[N],g[N],d[N];
int pt[N*4],delta[N*4];

void pushdown(int now,int l,int r,int mid)
{
    if (delta[now])
    {
        pt[now<<1]=delta[now<<1]=delta[now];
        pt[now<<1|1]=delta[now<<1|1]=delta[now];
        delta[now]=0;
    }
}
void change(int now,int l,int r,int lr,int rr,int v)
{
    int mid=(l+r)>>1;
    if (lr<=l&&r<=rr)
    {
        pt[now]=v;
        delta[now]=v;
        return;
    }
    pushdown(now,l,r,mid);
    if (lr<=mid) change(now<<1,l,mid,lr,rr,v);
    if (mid+1<=rr) change(now<<1|1,mid+1,r,lr,rr,v);
}
int query(int now,int l,int r,int x)
{
    int mid=(l+r)>>1;
    if (l==r) return pt[now];
    pushdown(now,l,r,mid);
    if (x<=mid) return query(now<<1,l,mid,x);
    else return query(now<<1|1,mid+1,r,x);
}
int find(int l,int r,int x,int j)
{
    int mid,ans=0;
    while (l<=r)
    {
        mid=(l+r)>>1;
        if (x<=s[j-1]-s[mid-1]) ans=mid,l=mid+1;
        else r=mid-1;
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;++i) scanf("%d",&w[i]),s[i]=s[i-1]+w[i];
    change(1,1,n,1,n,n+1);
    for (int i=n;i>=1;--i)
    {
        d[i]=query(1,1,n,i);
        g[i]=s[d[i]-1]-s[i-1];
        f[i]=f[d[i]]+1;
        int l=find(1,i-1,g[i],i);
        if (l) change(1,1,n,1,l,i);
    }
    printf("%d\n",f[1]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值