题目分析:
我们用
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[J−1]−Sum[I−1])
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[J−1]−Sum[I−1]),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[J−1]−Sum[I−1]的大小有关,也就是我们只需要考虑能够更新答案,能够发生状态转移的话那么必须满足下面的式子:
F
[
J
]
≤
S
u
m
[
J
−
1
]
−
S
u
m
[
I
−
1
]
F[J]\leq Sum[J-1]-Sum[I-1]
F[J]≤Sum[J−1]−Sum[I−1]
S
u
m
[
i
−
1
]
≤
S
u
m
[
J
−
1
]
−
F
[
J
]
Sum[i-1]\leq Sum[J-1]-F[J]
Sum[i−1]≤Sum[J−1]−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[K−1]−F[K]≥Sum[J−1]−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;
}