zoukankan      html  css  js  c++  java
  • [BZOJ2678][Usaco2012 Open]Bookshelf

    P.S. 偶然间发现可以用 markdown。。。

    [BZOJ2678][Usaco2012 Open]Bookshelf

    试题描述

    When Farmer John isn't milking cows, stacking haybales, lining up his cows, or building fences, he enjoys sitting down with a good book. Over the years, he has collected (N) books ((1 leq N leq 10^5)), and he wants to build a new set of bookshelves to hold them all. Each book (i) has a width (W(i)) and height (H(i)). The books need to be added to a set of shelves in order; for example, the first shelf should contain books (1...k) for some (k), the second shelf should start with book (k+1), and so on. Each shelf can have a total width of at most (L) ((1 leq L leq 10^9)). The height of a shelf is equal to the height of the tallest book on that shelf, and the height of the entire set of bookshelves is the sum of the heights of all the individual shelves, since they are all stacked vertically. Please help FJ compute the minimum possible height for the entire set of bookshelves. PROBLEM NAME: bookshelf

    输入

    • Line 1: Two space-separated integers: (N) and (L).

    • Lines 2..1+N: Line (i+1) contains two space-separated integers: (H(i)) and (W(i)). ((1 leq H(i) leq 10^6); (1 leq W(i) leq L)).

    输出

    • Line 1: The minimum possible total height for the set of bookshelves. SAMPLE

    输入示例

    5 10 //五本书,每个书架的宽度不超过10
    5 7 //第一本书的高度及宽度
    9 2
    8 5
    13 2
    3 8
    

    输出示例

    21
    

    数据规模及约定

    见“试题描述”和“输入

    题解

    算法1

    这题总体思路肯定是 dp,设 (f_i) 表示前 (i) 本书所需的最小高度和,则容易得到 (f_i = min{ f_j + max{ H_{j+1}, H_{j+2}, ... , H_i } | sum_{k=j+1}^i leq L })

    那么我们只需要考虑从哪个 (j) 转移到 (i) 就好了。

    于是我们可以维护一个 (H_i) 下降的单调栈,然后每次计算 (f_i) 的时候用前缀和 (S_i - S_j leq L) 的限制二分出 (j) 的位置(数组 (S) 表示高度的前缀和),然后就是查询单调栈中一段区间的最小值,可以用线段树维护。栈的插入、删除操作都可以当做线段树的点修改。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 100010
    #define oo 2147483647
    #define ool (1ll << 60)
    #define LL long long
    
    int n, hei[maxn], wid[maxn], lim;
    LL Sw[maxn];
    
    int S[maxn], mx[maxn], top;
    LL f[maxn];
    
    LL minv[maxn<<2];
    void update(int o, int l, int r, int p, LL v) {
    	if(l == r) minv[o] = v;
    	else {
    		int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
    		if(p <= mid) update(lc, l, mid, p, v);
    		else update(rc, mid + 1, r, p, v);
    		minv[o] = min(minv[lc], minv[rc]);
    	}
    	return ;
    }
    LL query(int o, int l, int r, int ql, int qr) {
    	if(l > r) return ool;
    	if(ql <= l && r <= qr) return minv[o];
    	int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
    	LL res = ool;
    	if(ql <= mid) res = min(res, query(lc, l, mid, ql, qr));
    	if(qr > mid) res = min(res, query(rc, mid + 1, r, ql, qr));
    	return res;
    }
    
    int main() {
    	n = read(); lim = read();
    	for(int i = 1; i <= n; i++) hei[i] = read(), wid[i] = read(), Sw[i] = Sw[i-1] + wid[i];
    	
    	f[0] = 0; S[0] = 0;
    	S[top = 1] = mx[1] = 0;
    	update(1, 1, n + 1, 1, 0);
    	for(int i = 1; i <= n; i++) {
    		while(top && hei[i] > mx[top]) top--;
    		S[++top] = i; mx[top] = hei[i];
    //		printf("stack: "); for(int j = 1; j <= top; j++) printf("%d%c", S[j], j < top ? ' ' : '
    ');
    		update(1, 1, n + 1, top, f[S[top-1]] + hei[i]);
    		int l = 1, r = top, pos = lower_bound(Sw, Sw + n + 1, Sw[i] - lim) - Sw;
    		while(l < r) {
    			int mid = l + r >> 1;
    			if(Sw[i] - Sw[S[mid]] > lim) l = mid + 1; else r = mid;
    		}
    		f[i] = min(query(1, 1, n + 1, l + 1, top), f[pos] + mx[l]);
    //		printf("%d %d | %d: %lld
    ", l, S[l], i, f[i]);
    	}
    	
    	printf("%lld
    ", f[n]);
    	
    	return 0;
    }
    
    算法2

    还是维护单调栈,我们发现这其实是一个双头队列,偶然在 UOJ 上发现了一个 (O(n)) 的做法。

    其实就是用两个栈模拟一个双头队列,注意到每个栈的前缀最小值是可以 (O(1)) 修改和询问的,所以用这种方法就可能将算法总复杂度降到 (O(n))

    我觉得最妙的地方在于重构,当某个栈空的时候将另一个栈分一半给这个已经空的栈(即暴力重构),复杂度有保证,详见上面链接(读者也不妨自己思考思考)。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 100010
    #define oo 2147483647
    #define ool (1ll << 60)
    #define LL long long
    
    int n, lim, hei[maxn], wid[maxn], lst[maxn]; // lst: last bigger height
    LL Sw[maxn];
    
    struct Info {
    	int pos, mxv; LL val;
    	Info() {}
    	Info(int _1, int _2, LL _3): pos(_1), mxv(_2), val(_3) {}
    } lft[maxn], rgt[maxn];
    int Sl, Sr;
    LL mnl[maxn], mnr[maxn];
    int rebuild() {
    	if(!Sl && !Sr) return -1;
    	if(!Sl) {
    		int mid = Sr + 1 >> 1;
    		for(int i = mid; i; i--) lft[++Sl] = rgt[i], mnl[Sl] = min(mnl[Sl-1], lft[Sl].val);
    		for(int i = mid + 1; i <= Sr; i++) rgt[i-mid] = rgt[i], mnr[i-mid] = min(mnr[i-mid-1], rgt[i-mid].val);
    		Sr -= mid;
    		return 0;
    	}
    	int mid = Sl + 1 >> 1;
    	for(int i = mid; i; i--) rgt[++Sr] = lft[i], mnr[Sr] = min(mnr[Sr-1], rgt[Sr].val);
    	for(int i = mid + 1; i <= Sl; i++) lft[i-mid] = lft[i], mnl[i-mid] = min(mnl[i-mid-1], lft[i-mid].val);
    	Sl -= mid;
    	return 0;
    }
    
    int q[maxn], hd, tl;
    
    LL f[maxn];
    
    int main() {
    	n = read(); lim = read();
    	for(int i = 1; i <= n; i++) hei[i] = read(), wid[i] = read(), Sw[i] = Sw[i-1] + wid[i];
    	
    	hei[0] = oo;
    	for(int i = 1; i <= n; i++) {
    		lst[i] = i - 1;
    		while(hei[lst[i]] <= hei[i]) lst[i] = lst[lst[i]];
    	}
    	mnl[0] = mnr[0] = ool;
    	rgt[++Sr] = Info(lst[1], hei[1], hei[1]);
    	mnr[1] = f[1] = hei[1];
    	q[hd = tl = 1] = 1;
    	int lft_edge = 0;
    	for(int i = 2; i <= n; i++) {
    		while(1) {
    			if(!Sr && rebuild()) break;
    			if(rgt[Sr].mxv > hei[i]) break;
    			Sr--;
    		}
    		rgt[++Sr] = Info(lst[i], hei[i], f[lst[i]] + hei[i]);
    		mnr[Sr] = min(mnr[Sr-1], rgt[Sr].val);
    		while(1) {
    			if(!Sl && rebuild()) break;
    			if(Sw[i] - Sw[lft[Sl].pos] <= lim) break;
    			Sl--;
    		}
    		
    		while(Sw[i] - Sw[lft_edge] > lim) lft_edge++;
    		while(hd <= tl && hei[i] >= hei[q[tl]]) tl--; q[++tl] = i;
    		while(lft_edge > q[hd]) hd++;
    		f[i] = min(min(mnl[Sl], mnr[Sr]), f[lft_edge] + hei[q[hd]]);
    //		printf("f[%d] = %lld
    ", i, f[i]);
    	}
    	
    	printf("%lld
    ", f[n]);
    	
    	return 0;
    }
    

    P.S. markdown 好难用。。。

  • 相关阅读:
    Jmeter Web 性能测试入门 (三):Jmeter 常用组件说明
    Jmeter Web 性能测试入门 (二):Fiddler 抓取 http/https 请求
    Jmeter Web 性能测试入门 (一):环境配置 (免安装版)
    Linux中MySQL配置文件my.cnf参数优化
    认识Docker
    Jmeter命令行方式启动
    ActiveMQ学习笔记(22)----ActiveMQ的优化和使用建议
    ActiveMQ学习笔记(21)----ActiveMQ集成Tomcat
    ActiveMQ学习笔记(20)----Consumer高级特性(二)
    ActiveMQ学习笔记(19)----Consumer高级特性(一)
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/7617024.html
Copyright © 2011-2022 走看看