P5095 [USACO12OPEN] Bookshelf S

发布时间 2023-07-23 20:10:38作者: 2020fengziyang

P5095 [USACO12OPEN] Bookshelf S

题目描述

Farmer John 闲来无事的时候总喜欢坐下来看书。这些年来,他一共收集了 \(N\) 本书(\(1 \leq N \leq 2000\)),他打算搭一共新的书架来装这些书。

每本书都有个宽度 \(w_i\) 和高度 \(h_i\),书必须按顺序来摆放(即同一层书架摆的书必须是连续的一个区间)。每层书架的总宽度不能超过 \(L\)\(1 \leq L \leq 10^9\)),每层书架的高度等于这一层最高的书的高度,整个书架的高度等于每层书架的高度之和。

现在请你帮 FJ 求出书架高度的最小值。

输入格式

第一行两个整数 \(N,L\)

接下来 \(N\) 行,每行两个整数 \(h_i,w_i\)\(1 \leq h_i \leq 10^6\)\(1 \leq w_i \leq L\))。

输出格式

输出一个整数,书架高度最小值。

样例 #1

样例输入 #1

5 10
5 7
9 2
8 5
13 2
3 8

样例输出 #1

21

提示

第一层放第一本书,第二层放第二,三,四本书,第三层放第五本书,总高度为 \(5+13+3=21\),可以证明不存在更优的方案。

思路

盲猜dp

一开始是两重的数组,要用线段树和 \(rmq\) 维护还不能过。

后面发现其实最终的层数和答案关系不大,关键的是高度

所以我们设 \(dp_i\) 表示放下前 \(i\) 本书的最小高度

那么

\[dp_i = MIN_{j = 1}^{i - 1} dp_j + MAX_{k = j}^ih_k \]

考试时我用了 \(rmq\) 维护后面的 \(h\) ,其实不用,只要 \(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;
}