当农夫约翰闲的没事干的时候,他喜欢坐下来看书。多年过去,他已经收集了 N 本书 (1 <= N <= 100,000), 他想造一个新的书架来装所有书。
每本书 i 都有宽度 W(i) 和高度 H(i)。书需要按顺序添加到一组书架上;比如说,第一层架子应该包含书籍1 ... k,第二层架子应该以第k + 1本书开始,以下如此。每层架子的总宽度最大为L(1≤L≤1,000,000,000)。每层的高度等于该层上最高的书的高度,并且整个书架的高度是所有层的高度的总和,因为它们都垂直堆叠。
请帮助农夫约翰计算整个书架的最小可能高度。
有N(1 <= N <= 100000)本书,每本书有一个宽度W(i),高度H(i),(1 <= H(i) <= 1,000,000; 1 <= W(i) <= L)。
现在有足够多的书架,书架宽度最多是L (1 <= L <= 1,000,000,000),把书按顺序(先放1,再放2.....)放入书架。某个书架的高度是该书架中所放的最高的书的高度。
将所有书放入书架后,求所有书架的高度和的最小值?
题目大意:一段元素,每个元素有两个权值h和w,要把它分成若干段(不限制段数),每一段的 $sum{w}$ 不能超过 L,最终的代价是每段的最大元素之和 $ sum{max_{h}} $ ,最小化代价
终于过了这个破题
第一次WA因为没判边界条件
第二次WA因为答案爆了int
第三次T了?没关系,进大牛开O2稳过
题解:f[i] 分完i这个数然后pia叽断开的最小高度和
f[i] = min{f[j]+max{h[j+1]..h[i]}} (sum[i]-sum[j]<=L)
我们发现对于一个固定的i,max{h[j+1]..h[i]}随j增大单调不增,f[j]随j增大单调不减
而且对于某一些区间,它们中的 max{h[j+1]..h[i]}是一样的,那么这段区间中的最优决策一定是把这一段区间要么全分到后面,要么全分到前面(暂时不考虑长度太长的情况)
那我们可以对所有的区间维护一下以它们的左边界-1位决策点的答案,最终的最优解一定在这些答案之中
然后考虑后面新增了一个数h[i],如果h[i]>h[j-1],那么j-1所在的那一段区间就没用了(它的最大h被迫上升了),把这一段区间和i这个位置合并,重复向前找区间,直到最大值比h[i]大位置,这是一个典型的单调队列从队尾踢元素的操作
然后考虑从队首踢元素,显然如果一个区间太靠左了,那么这个区间可能会因为sum[i]-sum[j]太大而无法取到
然后按照这个思路从队首删除元素就好了,有的时候是把一整个区间都删掉,有的时候是右移一个区间的左端点,注释应该写得挺清楚了
然后我们怎么维护答案呢?网上有的题解说用一颗平衡树实现,其实没那么烦,因为我们只要支持插入,删除,取最小值,维护一个双堆就行了
然而pq真是慢啊,要是不开O2会T两个点,开了O2是洛谷第2(毕竟堆比平衡树常数小多了),第1是个骗了数据的baka
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <algorithm> 5 #include <string> 6 #include <cstring> 7 #include <cmath> 8 #include <map> 9 #include <stack> 10 #include <set> 11 #include <vector> 12 #include <queue> 13 #include <time.h> 14 #define eps 1e-7 15 #define INF 0x3f3f3f3f 16 #define MOD 1000000007 17 #define rep0(j,n) for(int j=0;j<n;++j) 18 #define rep1(j,n) for(int j=1;j<=n;++j) 19 #define pb push_back 20 #define set0(n) memset(n,0,sizeof(n)) 21 #define ll long long 22 #define ull unsigned long long 23 #define iter(i,v) for(edge *i=head[v];i;i=i->nxt) 24 #define mp make_pair 25 #define max(a,b) (a>b?a:b) 26 #define min(a,b) (a<b?a:b) 27 #include<functional> 28 #define print_rumtime printf("Running time:%.3lfs ",double(clock())/1000.0); 29 #define TO(j) printf(#j": %d ",j); 30 //#define OJ 31 using namespace std; 32 const int MAXINT = 100010; 33 const int MAXNODE = 100010; 34 const int MAXEDGE = 2 * MAXNODE; 35 char BUF, *buf; 36 int read() { 37 char c = getchar(); int f = 1, x = 0; 38 while (!isdigit(c)) { if (c == '-') f = -1; c = getchar(); } 39 while (isdigit(c)) { x = x * 10 + c - '0'; c = getchar(); } 40 return f*x; 41 } 42 char get_ch() { 43 char c = getchar(); 44 while (!isalpha(c)) c = getchar(); 45 return c; 46 } 47 //------------------- Head Files ----------------------// 48 pair<int, int> q[MAXINT]; 49 struct pq { 50 priority_queue<ll,vector<ll>,greater<ll> > q, d; 51 void del(ll p) { 52 d.push(p); 53 } 54 void push(ll p) { 55 q.push(p); 56 } 57 ll top() { 58 while (!d.empty() && d.top() == q.top()) q.pop(), d.pop(); 59 return q.top(); 60 } 61 void pop() { 62 while (!d.empty() && d.top() == q.top()) q.pop(), d.pop(); 63 q.pop(); 64 } 65 }ans; 66 ll f[MAXINT], l,n; 67 int w[MAXINT], H[MAXINT]; 68 ll sum[MAXINT]; 69 void get_input(); 70 void work(); 71 int main() { 72 get_input(); 73 work(); 74 return 0; 75 } 76 void work() { 77 pair<int, int> *h, *t; 78 h = t = q; 79 *t++ = mp(0, INF);//哨兵元素! 80 rep1(i, n) { 81 int p = i; 82 while ((t - 1)->second<H[i]) { 83 ans.del((t - 1)->second + f[(t - 1)->first - 1]); 84 p = (t - 1)->first; 85 t--; 86 } 87 *t++ = mp(p, H[i]); 88 ans.push(f[p - 1] + H[i]); 89 h++; 90 while ( (h + 1<t) && sum[i] - sum[(h + 1)->first - 1]>l) ans.del(h->second + f[h->first - 1]), h++; //delete the whole segment 91 ans.del(h->second + f[h->first - 1]); 92 for (; sum[i] - sum[h->first-1]>l; h->first++);//delete some element 93 if ((h+1<t)&&h->first == (h + 1)->first) h++; 94 else ans.push(h->second + f[h->first - 1]); 95 h--; 96 *h = mp(0, INF); 97 f[i] = ans.top(); 98 } 99 printf("%lld ", f[n]); 100 } 101 void get_input() { 102 n = read(); l = read(); 103 rep1(i, n) H[i] = read(), w[i] = read(), sum[i] = sum[i - 1] + w[i]; 104 }