问题描述:
若有干个干草, 分别有各自的宽度, 要求将它们按顺序摆放, 并且每层的宽度不大于 它的下面一层 , 求最多叠几层
题解:
zkw神牛证明了: 底边最短, 层数最高 证明: 传送门
接下来我们就可以根据这个结论进行dp。 前缀和sum, 以及 F[ i ]第 i 个数之后的干草叠起来后, 底层的最短宽度, 以及 H[ i ] 表示 第i个后的干草堆最高叠几层
有转移方程 : F[ i ] = min( sum[ j - 1] - sum[i - 1] ) ( j > i && sum[ j - 1] - sum[ i - 1] >= f[ j ] ) 由于前缀和从前往后是递增的, 所以 j 越小越好。
又因为要满足 sum[ j - 1] - f[ j ] >= sum[ i - 1 ] , 所以 sum[ j - 1] - f[ j ] 越大越好, 可以用单调队列来使决策具有单调性, 每次取出队首就是最优决策
代码
1 #include<cstring> 2 #include<cstdio> 3 #include<algorithm> 4 #define rd read() 5 #define rep(i,a,b) for( int i = (a); i <= (b); ++i ) 6 #define per(i,a,b) for( int i = (a); i >= (b); --i ) 7 using namespace std; 8 9 const int N = 1e5 + 1e4; 10 11 int n, a[N], sum[N], f[N], h[N], q[N]; 12 13 int read() { 14 int X = 0, p = 1; char c = getchar(); 15 for(; c > '9' || c < '0'; c = getchar() ) if( c == '-' ) p = -1; 16 for(; c >= '0' && c <= '9'; c = getchar() ) X = X * 10 + c - '0'; 17 return X * p; 18 } 19 20 int main() 21 { 22 n = rd; 23 rep( i, 1, n ) sum[i] = sum[i - 1] + rd; 24 int l = 1, r = 1; 25 q[r] = n + 1; 26 sum[n + 1] = sum[n]; 27 per( i, n, 1 ) { 28 while( l < r && f[ q[l + 1] ] <= sum[ q[l + 1] - 1] - sum[i - 1] ) l++; 29 f[i] = sum[ q[l] - 1] - sum[i - 1]; 30 h[i] = h[q[l]] + 1; 31 while( l < r && sum[q[r] - 1] - f[q[r]] <= sum[i - 1] - f[i] ) r--; 32 q[++r] = i; 33 } 34 printf("%d ",h[1]); 35 }