1. 程式人生 > >P2900 [USACO08MAR]土地徵用Land Acquisition

P2900 [USACO08MAR]土地徵用Land Acquisition

\(\color{#0066ff}{ 題目描述 }\)

約翰準備擴大他的農場,眼前他正在考慮購買N塊長方形的土地。如果約翰單買一塊土 地,價格就是土地的面積。但他可以選擇併購一組土地,併購的價格為這些土地中最大的長 乘以最大的寬。比如約翰併購一塊3 × 5和一塊5 × 3的土地,他只需要支付5 × 5 = 25元, 比單買合算。 約翰希望買下所有的土地。他發現,將這些土地分成不同的小組來併購可以節省經費。 給定每份土地的尺寸,請你幫助他計算購買所有土地所需的最小費用。

\(\color{#0066ff}{輸入格式}\)

Line 1: A single integer: N

Lines 2..N+1: Line i+1 describes plot i with two space-separated integers: \(width_i\)

and \(length_i\)

\(\color{#0066ff}{輸出格式}\)

Line 1: The minimum amount necessary to buy all the plots.

\(\color{#0066ff}{輸入樣例}\)

4 
100 1 
15 15 
20 5 
1 100 

\(\color{#0066ff}{輸出樣例}\)

500 

\(\color{#0066ff}{資料範圍與提示}\)

none

\(\color{#0066ff}{題解}\)

首先可以發現,對於兩個矩形, 如果一個可以完全包含另一個,完全可以讓這兩個一組,這樣那個小的矩形就沒貢獻了,可以刪掉

按長為第一關鍵字,寬為第二關鍵字從小到大排序

倒著掃一遍,維護max寬,如果當前矩形的寬小於max寬,又因為排序一定小於長

所以當前矩形就沒用了,否則放入一個新的數組裡

翻轉一下可以發現

這個陣列的矩形,長遞增,寬遞減

最優的分組一定是幾個連續的區間

可以發現一個區間的價值是l的長和r的寬

如果不連續,那麼價值要比這個大

所以就可以DP了

\(f[i] = f[j-1]+i_a*j_b\)

\(i_a\)作為斜率移到左邊,因為是負的,而且還要維護最小值,斜率應為正的

可以直接取正的維護最大值然最後取反就行了

#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; int x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
const int maxn = 50505;
using std::pair;
using std::make_pair;
pair<LL, LL> mp[maxn], a[maxn];
int cnt;
LL f[maxn];
LL X(int x) { return a[x + 1].second; }
LL Y(int x) { return f[x]; }
double K(int x, int y) { return (double)(Y(x) - Y(y)) / (double)(X(x) - X(y)); }
int main() {
    int n = in();
    for(int i = 1; i <= n; i++) mp[i].first = in(), mp[i].second = in();
    std::sort(mp + 1, mp + n + 1);
    LL max = 0;
    for(int i = n; i >= 1; i--) {
        if(mp[i].second > max) a[++cnt] = mp[i];
        max = std::max(max, mp[i].second);
    }
    std::reverse(a + 1, a + cnt + 1);
    static int q[maxn], head, tail;
    for(int i = 1; i <= cnt; i++) {
        LL k = a[i].first;
        while(head < tail && k > K(q[head], q[head + 1])) head++;
        f[i] = f[q[head]] - a[i].first * a[q[head] + 1].second;
        while(head < tail && K(q[tail], q[tail - 1]) > K(i, q[tail - 1])) tail--;
        q[++tail] = i;
    }
    printf("%lld\n", -f[cnt]);
    return 0;
}