• [USACO 08MAR]土地购买


    Description

    题库链接

    给你 (n) 块不同大小的土地。你可分批购买这些土地,每一批价格为这一批中最大的长乘最大的宽。问你买下所有土地的花费最小为多少。

    (1leq nleq 50000)

    Solution

    有一个贪心的思想就是一块土地如果是 (w_i imes h_i),若 (exists j) 满足 (w_ileq w_j,h_ileq h_j)。那么 (i) 这块土地一定不会参与到花费计算的。这是因为 (i) 这块土地可以放在 (j) 那一批,然后 (i) 的长和宽一定不是最大的。

    因此我们把这些土地按 (w) 从小到大排序,那么 (h) 一定是单调递减的。基于此,我们还可以得出另外一个结论,同一批次在这个序列中一定是连续的一段。这是因为这一段的 (w,h) 最大值一定是在端点取,你把中间某个地不放在这一段是对这一段的花费不产生影响的,因此不如选上这一个点。

    基于上述两个结论,我们可以对排完序的序列进行 DP。记 (f_i) 表示购买 (1sim i) 的土地的最小的花费,那么 (f_i=min{f_j+w_i imes h_j})

    由于 (w_i)(h_j) 单调性相反,因此可以用单调队列维护上凸包斜率优化解决。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 50000+5;
    
    int n, tot, q[N], head, tail;
    struct tt {
        int w, h;
        bool operator < (const tt &b) const {
            return w == b.w ? h < b.h : w < b.w;
        }
    } a[N], b[N];
    long long f[N];
    
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) scanf("%d%d", &a[i].w, &a[i].h);
        sort(a+1, a+n+1);
        for (int i = 1; i <= n; i++) {
            while (tot && b[tot].h <= a[i].h) --tot;
            b[++tot] = a[i];
        } 
        n = tot; tail = -1;
        for (int i = 1; i <= n; i++) {
            while (head < tail && (-f[i-1]+f[q[tail]-1])*(b[i].h-b[q[tail-1]].h)
            <= (-f[i-1]+f[q[tail-1]-1])*(b[i].h-b[q[tail]].h)) --tail;
            q[++tail] = i;
            while (head < tail && -f[q[head]-1]+f[q[head+1]-1] <= 1ll*b[i].w*(b[q[head]].h-b[q[head+1]].h)) ++head;
            f[i] = f[q[head]-1]+1ll*b[q[head]].h*b[i].w;
        }
        printf("%lld
    ", f[n]);
        return 0;
    }
  • 相关阅读:
    栈:逆波兰表达式(后缀表达式)
    栈:实现综合计算器(中缀表达式)
    栈:数组模拟栈
    链表(Linked List):单向环形链表
    单链表常见面试题
    链表(Linked List):双向链表
    链表(Linked List): 单链表
    队列和环形队列
    稀疏数组
    Linux命令--pushd和popd
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/12257675.html
Copyright © 2020-2023  润新知