P5095 [USACO12OPEN] Bookshelf S
文章目录
- P5095 [USACO12OPEN] Bookshelf S
- 题目描述
- 输入格式
- 输出格式
- 样例 #1
- 样例输入 #1
- 样例输出 #1
- 提示
- 思路
- 赛时 code
- 别人code
题目描述
Farmer John 闲来无事的时候总喜欢坐下来看书。这些年来,他一共收集了 N N N 本书( 1 ≤ N ≤ 2000 1 \leq N \leq 2000 1≤N≤2000),他打算搭一共新的书架来装这些书。
每本书都有个宽度 w i w_i wi 和高度 h i h_i hi,书必须按顺序来摆放(即同一层书架摆的书必须是连续的一个区间)。每层书架的总宽度不能超过 L L L( 1 ≤ L ≤ 1 0 9 1 \leq L \leq 10^9 1≤L≤109),每层书架的高度等于这一层最高的书的高度,整个书架的高度等于每层书架的高度之和。
现在请你帮 FJ 求出书架高度的最小值。
输入格式
第一行两个整数 N , L N,L N,L。
接下来 N N N 行,每行两个整数 h i , w i h_i,w_i hi,wi( 1 ≤ h i ≤ 1 0 6 1 \leq h_i \leq 10^6 1≤hi≤106, 1 ≤ w i ≤ L 1 \leq w_i \leq L 1≤wi≤L)。
输出格式
输出一个整数,书架高度最小值。
样例 #1
样例输入 #1
5 10
5 7
9 2
8 5
13 2
3 8
样例输出 #1
21
提示
第一层放第一本书,第二层放第二,三,四本书,第三层放第五本书,总高度为 5 + 13 + 3 = 21 5+13+3=21 5+13+3=21,可以证明不存在更优的方案。
思路
盲猜dp
一开始是两重的数组,要用线段树和 r m q rmq rmq 维护还不能过。
后面发现其实最终的层数和答案关系不大,关键的是高度
所以我们设 d p i dp_i dpi 表示放下前 i i i 本书的最小高度
那么
d p i = M I N j = 1 i − 1 d p j + M A X k = j i h k dp_i = MIN_{j = 1}^{i - 1} dp_j + MAX_{k = j}^ih_k dpi=MINj=1i−1dpj+MAXk=jihk
考试时我用了 r m q rmq rmq 维护后面的 h h h ,其实不用,只要 j j j 从后枚举维护最大值就好了。
赛时 code
#include <bits/stdc++.h>
#define fu(x , y , z) for(int x = y ; x <= z ; x ++)
#define fd(x , y , z) for(int x = y ; x >= z ; x --)
#define LL long long
using namespace std;
const int N = 2005;
LL l , h[N] , w[N] , sq[35] , rmq[N][35] , ans , s[N] , dp[N];
int n;
void pre () {sq[0] = 1;for (int i = 1 ; i <= 30 ; i ++) sq[i] = sq[i - 1] * 2;fu (i , 1 , n) rmq[i][0] = h[i];fu (j , 1 , 30) {fu (i , 1 , n) {if (i + sq[j] - 1 > n) break;rmq[i][j] = max (rmq[i][j - 1] , rmq[i + sq[j - 1]][j - 1]);}}
}
LL find (int x , int y) {int lst = 30 , z;LL ansf = 0;while (x <= y) {for (int i = lst ; i >= 0 ; i --) {if (x + sq[i] - 1 > y) continue;ansf = max (ansf , rmq[x][i]);x = x + sq[i];z = i;}lst = z;}ansf = max (ansf , h[y]);return ansf;
}
int main () {scanf ("%d%lld" , &n , &l);fu (i , 1 , n)scanf ("%lld%lld" , &h[i] , &w[i]);pre ();fu (i , 1 , n) s[i] = s[i - 1] + w[i];fu (i , 1 , n) dp[i] = 1e18 + 5;fu (i , 1 , n) {fd (j , i - 1 , 0) {if (s[i] - s[j] > l) break;dp[i] = min (dp[i] , dp[j] + find (j + 1 , i));}}printf ("%lld" , dp[n]);return 0;
}
别人code
#include <bits/stdc++.h>
using namespace std;
const int N = 2002;
int n, m, p, k, ans, sum, tot, cnt, a[N], b[N], f[N], c[N], l;
int main()
{std::cin>> n >> l;for(int i = 1; i <= n; i++){std::cin >> a[i] >> b[i];c[i] = c[i - 1] + b[i];f[i] = 1e9;}for(int i = 1; i <= n; i++){f[i] = f[i - 1] + a[i];tot = a[i];for(int j = i - 1; j >= 1; j--){if(c[i] - c[j - 1] > l)break;tot = max(tot, a[j]);f[i] = min(f[i], f[j - 1] + tot);}}cout << f[n];return 0;
}