BZOJ P1233 干草堆tower 【单调队列优化DP】

题目分析:

我们用 F [ I ] F[I] F[I]表示用第 I   N I~N I N包做草堆的时候最底下一层的最短长度, G [ I ] G[I] G[I]记录此时能到达的最高高度,显然可以得到如下的一个状态转移方程:
i f ( J > I , F [ J ] < = S u m [ J − 1 ] − S u m [ I − 1 ] ) if(J>I , F[J]<=Sum[J-1]-Sum[I-1]) if(J>I,F[J]<=Sum[J1]Sum[I1])
F [ I ] = m i n ( S u m [ J − 1 ] − S u m [ I − 1 ] ) , G [ I ] = G [ J ] + 1 F[I]=min(Sum[J-1]-Sum[I-1]),G[I]=G[J]+1 F[I]=min(Sum[J1]Sum[I1]),G[I]=G[J]+1
由于这样的做法时间复杂度是 n 2 n^2 n2,所以我们考虑优化 D P DP DP。我们注意到 F [ I ] F[I] F[I]的值总是只与 S u m [ J − 1 ] − S u m [ I − 1 ] Sum[J-1]-Sum[I-1] Sum[J1]Sum[I1]的大小有关,也就是我们只需要考虑能够更新答案,能够发生状态转移的话那么必须满足下面的式子:
F [ J ] ≤ S u m [ J − 1 ] − S u m [ I − 1 ] F[J]\leq Sum[J-1]-Sum[I-1] F[J]Sum[J1]Sum[I1]
S u m [ i − 1 ] ≤ S u m [ J − 1 ] − F [ J ] Sum[i-1]\leq Sum[J-1]-F[J] Sum[i1]Sum[J1]F[J]
通过这个式子我们可以发现如果能够更新答案我们一定要满足式子右边尽量小,也就是说,如果存在以下的式子,那么一定不能够更新答案:
K > J , S u m [ K − 1 ] − F [ K ] ≥ S u m [ J − 1 ] − F [ J ] K>J,Sum[K-1]-F[K] \geq Sum[J-1]-F[J] K>J,Sum[K1]F[K]Sum[J1]F[J]
分析到这里,一个单调队列优化 D P DP DP的模型也就涌现出来了,下面给出参考代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define DB double
#define SG string
#define LL long long
#define Fp(A,B,C,D) for(A=B;A<=C;A+=D)
#define Fm(A,B,C,D) for(A=B;A>=C;A-=D)
#define Clear(A) memset(A,0,sizeof(A))
#define Copy(A,B) memcpy(A,B,sizeof(A))
using namespace std;
const LL Max=1e5+5;
const LL Mod=1e9+7;
const LL Inf=1e18;
LL N,F[Max],G[Max],Q[Max],Sum[Max];
inline LL Read(){
	LL X=0;char CH=getchar();bool F=0;
	while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
	while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
	return F?-X:X;
}
inline void Write(LL X){
	if(X<0)X=-X,putchar('-');
	if(X>9)Write(X/10);
	putchar(X%10+48);
}
int main(){
	LL I,J,K;
	N=Read();
	Fp(I,1,N,1){
		Sum[I]=Sum[I-1]+Read();
	}LL Head=1,Tail=1;
	Q[1]=N+1;
	Fm(I,N,1,1){
		while(Head<Tail&&Sum[Q[Head+1]-1]-Sum[I-1]>=F[Q[Head+1]]){
			++Head;
		}F[I]=Sum[Q[Head]-1]-Sum[I-1];G[I]=G[Q[Head]]+1;
		while(Head<=Tail&&Sum[Q[Tail]-1]-F[Q[Tail]]<=Sum[I-1]-F[I]){
			--Tail;
		}Q[++Tail]=I;
	}Write(G[1]);
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值