COGS-363-土地购买-斜率优化

来源:互联网 发布:高频注入源码 编辑:程序博客网 时间:2024/05/15 20:57

描述

  • 有 n(≤50000) 块 (Xi*Yi) 的土地. 一些土地的购买价格是这些土地中长的最大值乘宽的最大值 (长宽不可颠倒). 求购买所有土地的最小花费.
  • 将 n 个二元组 (x, y) 分组使每个组中的 max{x}*max{y} 的和最小.

分析

  • 这里土地的顺序是无所谓的, 所以凭直觉先按 x 升序排序.
  • 然后去除无用的土地, 即如果一块土地可以完全包含在另一块土地里, 这一块土地就不需要再考虑了(因为它可以和后者分在一组). 这一步可以用一个单调栈来实现.

  • 这时 x 递增, y 递减.

  • 得到转移方程 : 由 j->i(j < i), f[i]=f[j1]+XiYj
  • 意义就在于 j 和 i 之间的土地的长不大于 Xi 而宽不大于 Yj.
  • O(n2)

  • 斜率优化 :
  • 如果 j < k, j 不比 k 差, 则 f[j1]+XiYjf[k1]+XiYk
  • 化简得到 Xif[k1]f[j1]YjYk
  • 设上式 f[k1]f[j1]YjYk=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 的 XY 的值来维护单调队列. 其实是判定队尾结点可不可以删除.
  • 而在更新 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(nlogn)

代码

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;}
1 0
原创粉丝点击