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

本文介绍了一种使用单调队列优化动态规划(DP)的方法,以解决特定类型的最短路径问题。通过分析状态转移方程,推导出了优化条件,并给出了实现代码,有效地将时间复杂度从O(n^2)降低到更优级别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目分析:

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

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值