1. 程式人生 > >題解【土地征用_USACO08MAR】(洛谷P2900)

題解【土地征用_USACO08MAR】(洛谷P2900)

得出 兩種 include 復雜 can pla ret names \n

題意

需要購買\(n\)塊土地,每塊土地有一個長\(r_i\)與寬\(c_i\)。可以將這些土地打成幾包購買。每包土地的價格為這包土地中最大的長乘以最大的寬。求最小花費。

分析一下

  • \(DP\)沒話說,看數據範圍知道要用斜率優化了。
  • \(F_i\)表示處理完第\(i\)塊土地的最小花費。
  • 轉移就是枚舉之前的一塊土地\(j\),將\([j + 1\cdots i]\)全部看成一包進行轉移,即:
    \[F_i = min\{F_j + r_{j+1} \cdot c_i\}\]
  • 這個轉移成立的前提是\(c\)數組要從小到大排序,才能在轉移時\(c_i\)最大。
  • 再有就是預先把那些直接被包含的土地(長與寬都小於其他任意一塊土地)刪掉,買了也對轉移沒用。

優化過程

  • 啊啦斜率優化原理什麽的不太清楚啦~
  • 只知道按步驟來不怎麽會錯啦~
  1. 設狀態\(i\),有兩種轉移\(j,k\),且\(j < k < i\)\(k\)更優。即:
    \[F_k + r_{k + 1} \cdot c_i < F_j + r_{j + 1} \cdot c_i\]
  2. 移項,變為左邊與\(i\)有關的不等式:
    \[c_i \cdot (r_{k+1} - r_{j + 1}) < F_j - F_k\]
    \[\because r_{k + 1} \geq r{j + 1}\]
    \[\therefore c_i < \frac{F_j - F_k}{r_{k + 1} - r_{j + 1}}\]
  3. 所以只有滿足上面這個不等式才能保證\(k\)\(j\)更優。
  4. 維護一個單調隊列。隊首維護時間,只有當隊首與隊首\(+1\)不滿足於上面那個不等式時,證明隊首不比第二個優,隊首就可以出隊了。
  5. 此時隊首一定是最優的,用這個隊首來更新答案。(就相當於原來的轉移方程枚舉\(j\)的過程)
  6. 隊尾維護單調性。當新加入一個元素\(i\)時,將隊列最後兩個值代入不等式的右邊而得出的值與隊尾和\(i\)代入不等式的右邊得出的值比較,如果右邊小於左邊,說明\(i\)比隊尾更優,則將隊尾踢出。(與題面中求最小值一致)
  7. \(i\)加入隊列。
  • 對於每個元素,共進隊一次,出隊一次,時間復雜度\(O(n)\)

代碼君

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int MAXN = 50005;
struct Earth {
    long long r, c;
    bool operator < (const Earth &res) const {return c < res.c;}
} ar[MAXN], a[MAXN]; //結構體,ar為踢去沒用的土地後的數組。 
int n, N;
long long f[MAXN];
int head, tail, que[MAXN * 5];

inline double g(int j, int k) {
    return (f[j] - f[k]) / (ar[k + 1].r - ar[j + 1].r);
} //將不等式的右邊寫成函數。 

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%lld%lld", &a[i].r, &a[i].c);
    sort(a + 1, a + n + 1);
    for (int i = 1; i <= n; i++) {
        while (N && ar[N].r <= a[i].r)
            N--; //如果寬比前面的大(排序決定),長也比前面大,就將前面的踢出(單調棧)。 
        ar[++N] = a[i];
    }
    head = tail = 0;
    que[0] = 0, /*隊列裏存放的是數組下標*/f[0] = 0;
    for (int i = 1; i <= N; i++) {
        while (head < tail && ar[i].c >= g(que[head], que[head + 1]))
            head++; //維護時間 
        f[i] = f[que[head]] + ar[que[head] + 1].r * ar[i].c; //更新答案 
        while (head < tail && g(que[tail - 1], que[tail]) >= g(que[tail], i))
            tail--; //維護單調性 
        que[++tail] = i; //加入元素 
    }
    printf("%lld\n", f[N]);
    return 0;
}

題解【土地征用_USACO08MAR】(洛谷P2900)