题目描述
(Farmer John)准备扩大他的农场,眼前他正在考虑购买(N)块长方形的土地。
如果(FJ)单买一块土地,价格就是土地的面积。但他可以选择并购一组土地,并购的价格为这些土地中最大的长乘以最大的宽。比如(FJ)并购一块(3 imes 5) 和一块 (5 imes 3)的土地,他只需要支付(5 imes 5=25)元,比单买合算。
(FJ)希望买下所有的土地。他发现,将这些土地分成不同的小组来并购可以节省经费。 给定每份土地的尺寸,请你帮助他计算购买所有土地所需的最小费用。
题解
很容易想到如果一块土地的长和宽都比另一块小,那么这两块土地合并一下并按照大的那块土地计算比分开计算优。
所以我们先将土地按照长度从小到大排序,再对它们的高度维护一个单调递减栈,最后在栈中的土地才是有意义的。
对于这些土地,我们进行(dp),设(f[i])表示考虑到第(i)块土地的最小费用,状态转移方程(f[i] = min(f[i], f[j] + h[j + 1] * l[i])[1 leq j < i])
,这个转移过程是(O(n ^2))的,显然不可行。
所以我们考虑斜率优化,假设(1leq k < j < i),且对于(i)来说,(j)比(k)优,那么
(f[j] + h[j + 1] * l[i] leq f[k] + h[k + 1] * l[i])
(f[j] - f[k] leq (h[k + 1] - h[j + 1]) * l[i])
(frac{f[j] - f[k]}{- h[j + 1] - (- h[k + 1])} leq l[i])
所以以此为判断条件弹出不优的队首并依次更新即可。
#include <iostream>
#include <cstdio>
#include <algorithm>
#define ll long long
#define int long long
using namespace std;
const int N = 50005;
int n, top, sta[N], q[N], l, r;
ll f[N];
struct node{int x, y;}a[N], b[N];
inline int read()
{
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
bool cmp(const node & a, const node & b) {return a.x < b.x;}
int X(int i) {return - b[i + 1].y;}
ll Y(int i) {return f[i];}
double slope(int i, int j) {return (double)(Y(i) - Y(j)) / (double)(X(i) - X(j));}
void work()
{
n = read();
for(int i = 1; i <= n; i ++) {a[i].x = read(); a[i].y = read();}
sort(a + 1, a + n + 1, cmp);
for(int i = 1; i <= n; i ++)
{
while(top && a[sta[top]].y <= a[i].y) top --;
sta[++ top] = i;
}
for(int i = 1; i <= top; i ++) b[i] = a[sta[i]];
q[l = r = 1] = 0;
for(int i = 1; i <= top; i ++)
{
while(l < r && slope(q[l], q[l + 1]) <= b[i].x) l ++;
f[i] = f[q[l]] + (ll)b[q[l] + 1].y * b[i].x;
while(l < r && slope(q[r - 1], q[r]) >= slope(q[r - 1], i)) r --;
q[++ r] = i;
}
printf("%lld
", f[top]);//top
}
signed main() {return work(), 0; }