版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_21110267/article/details/45114279
描述
- 有 n(≤50000) 块 (Xi*Yi) 的土地. 一些土地的购买价格是这些土地中长的最大值乘宽的最大值 (长宽不可颠倒). 求购买所有土地的最小花费.
- 将 n 个二元组 (x, y) 分组使每个组中的 max{x}*max{y} 的和最小.
分析
- 这里土地的顺序是无所谓的, 所以凭直觉先按 x 升序排序.
然后去除无用的土地, 即如果一块土地可以完全包含在另一块土地里, 这一块土地就不需要再考虑了(因为它可以和后者分在一组). 这一步可以用一个单调栈来实现.
这时 x 递增, y 递减.
- 得到转移方程 : 由 j->i(j < i),
f[i]=f[j−1]+Xi∗Yj - 意义就在于 j 和 i 之间的土地的长不大于
Xi 而宽不大于Yj . -
O(n2)
- 斜率优化 :
- 如果 j < k, j 不比 k 差, 则
f[j−1]+Xi∗Yj≤f[k−1]+Xi∗Yk - 化简得到
Xi≤f[k−1]−f[j−1]Yj−Yk - 设上式
f[k−1]−f[j−1]Yj−Yk=g(j,k) - 则如果
g(a,b)>g(b,c) 则 b 可以完全被舍弃. 证明如下
- 如果
g(a,b)≥Xi , 那么 a 优于 b. - 如果
g(a,b)<Xi , 那么g(b,c)<Xi c 优于 b.
- 如果
- 所以可以用一个单调队列来存储当前在考虑范围内的土地. 用队尾的两个结点和 i 的
X 和Y 的值来维护单调队列. 其实是判定队尾结点可不可以删除. - 而在更新
f[i] 的答案时, 则从队头选取. 如果g(q[first],q[first+1])≥Xi 说明队首元素是队列中的最优解(向后推就可以证明了). 否则first 就应该从队列中删除, 因为此时q[first+1] 比q[first] 优, 在 i 变大时Xi 也变大, 那么g(q[first],q[first+1])<Xi 是一定成立的. -
O(n∗logn)
代码
https://code.csdn.net/snippets/647122
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 50000 + 10;
inline int getint() {
int x = 0, f = 1;
char ch = getchar();
for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x*10 + ch-'0';
return x * f;
}
struct Node {
int x, y;
bool operator < (const Node& rhs) const {
if(x == rhs.x) return y < rhs.y;
return x < rhs.x;
}
} A[maxn];
int q[maxn];
long long f[maxn];
double g(int a, int b) {
return (double)(f[b-1]-f[a-1]) / (A[a].y-A[b].y);
}
int main() {
freopen("acquire.in", "r", stdin);
freopen("acquire.out", "w", stdout);
int n = getint(), m;
for(int i = 0; i < n; i++) {
A[i].x = getint();
A[i].y = getint();
}
sort(A, A + n);
m = 0;
for(int i = 0; i < n; i++) {
while(m > 0 && A[m-1].y <= A[i].y) m--;
A[m++] = A[i];
}
int first, last;
first = last = 0;
for(int i = 0; i < m; i++) {
while(last-first > 1 && g(q[last-2], q[last-1]) > g(q[last-1], i)) last--;
q[last++] = i;
while(last-first > 1 && g(q[first], q[first+1]) < (double)A[i].x) first++;
int j = q[first];
f[i] = (long long)A[i].x * A[j].y + (j > 0 ? f[j-1] : 0);
}
printf("%lld\n", f[m-1]);
return 0;
}