BZOJ1233 [Usaco2009Open]干草堆tower[贪心+单调队列优化]

地址


很巧妙的一道题。不再是决策点以dp值中一部分含j项为维护对象,而是通过维护条件来获取决策。

首先有个贪心策略,让底层的宽度尽可能小,才能让高度尽可能高。所以应该倒着dp,表示堆$i~n$的最高高度$f[i]$,同时这种最值应来源于之后的j,要在设一个$g[i]$表示以i为底层,最窄的宽度。这个的话真的只可意会啊。

所以dp方程就能出来了

$g[i]=min(sum[j-1]-sum[i-1])   g[j]<=sum[j-1]-sum[i-1]$

$f[i]=f[j]+1$

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 typedef long long ll;
 8 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;}
 9 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;}
10 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
11 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
12 template<typename T>inline T read(T&x){
13     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
14     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
15 }
16 const int N=100000+7;
17 int sum[N],f[N],g[N],q[N];
18 int n,l,r;
19 
20 int main(){//freopen("test.in","r",stdin);//freopen("tmp.out","w",stdout);
21     read(n);for(register int i=1;i<=n;++i)sum[i]=read(sum[i])+sum[i-1];
22     l=1,r=1;q[l]=n+1;
23     for(register int i=n;i;--i){
24         while(l<r&&sum[q[l+1]-1]-g[q[l+1]]>=sum[i-1])++l;
25         g[i]=sum[q[l]-1]-sum[i-1],f[i]=f[q[l]]+1;
26         while(l<=r&&sum[q[r]-1]-g[q[r]]<=sum[i-1]-g[i])--r;
27         q[++r]=i;
28     }
29     printf("%d\n",f[1]);
30     return 0;
31 }

猜你喜欢

转载自www.cnblogs.com/saigyouji-yuyuko/p/10453611.html