【NOIP2012模拟11.1】塔(加强)

题目

玩完骰子游戏之后,你已经不满足于骰子游戏了,你要玩更高级的游戏。
今天你瞄准了下述的好玩的游戏:
首先是主角:塔。你有N座塔一列排开。每座塔各自有高度,有可能相等。
这个游戏就不需要地图了。
你每次可以选择相邻的两座塔合并在一起,即这两座塔的高度叠加后变成了同一座塔。然后原本分别与这两座塔相邻的塔变得与这座新的塔相邻。
你的目标是在使用最少的操作次数在游戏的最后获得一列塔,这些塔的高度从左到右形成一个不下降的数列。

分析

这是到结论题。。。
结论一:每个块越小越好。
so,设\(f_i\)表示处理完了i的最小操作次数。再设\(h_i\)表示最优情况下的\(g_i\)的i所在的塔的高度最小值。
转移为:
\[f_i=min(f_i,f_j+i-j-1(g_j<=sum(j+1...i)))\]
这是\(O(n^2)\)的。
我们想办法优化它,
首先,因为\(f_i+1>=f_{i+1}\)
结论二,当我们枚举j从后往前搜,当高度合法,就是最优的答案,也就可以break。
但是,这还是超时了。
我们打个单调队列。

#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
const long long maxlongint=2147483647;
const long long mo=1000000007;
const long long N=1000025;
using namespace std;
long long f[N],a[N],n,maxa,ans=maxlongint,sum[N],g[N],d[N];
int main()
{
    memset(f,0,sizeof(f));
    memset(g,0,sizeof(g));
    scanf("%lld",&n);
    f[0]=g[0]=0;
    long long l,r;
    d[l=r=1]=0;
    for(long long i=1;i<=n;i++) scanf("%lld",&a[i]),maxa=max(a[i],maxa),sum[i]=sum[i-1]+a[i];
    for(long long i=1;i<=n;i++)
    {
            while(g[d[l+1]]+sum[d[l+1]]<=sum[i] && l<r) l++;
            long long sigma=sum[i]-sum[d[l]];
            f[i]=f[d[l]]+i-d[l]-1;
            g[i]=sigma;
            while(g[d[r]]+sum[d[r]]>=sum[i]+g[i] && l<=r) r--;
            d[++r]=i;
    }
    printf("%lld",f[n]);
}

猜你喜欢

转载自www.cnblogs.com/chen1352/p/9045305.html