1. 程式人生 > >bzoj1597 [Usaco2008 Mar]土地購買

bzoj1597 [Usaco2008 Mar]土地購買

bzoj1597 需要 usaco desc 每一個 post inline cmp 專題

[Usaco2008 Mar]土地購買

Time Limit: 10 Sec Memory Limit: 162 MB

Description

農夫John準備擴大他的農場,他正在考慮\(N (1 <= N <= 50,000)\)塊長方形的土地. 每塊土地的長寬滿足\((1 <= 寬 < = 1,000,000; 1 <= 長 <= 1,000,000)\). 每塊土地的價格是它的面積,但FJ可以同時購買多快土地. 這些土地的價
格是它們最大的長乘以它們最大的寬, 但是土地的長寬不能交換. 如果FJ買一塊\(3*5\)的地和一塊\(5*3\)的地,則他需要
\(5*5=25\). FJ希望買下所有的土地,但是他發現分組來買這些土地可以節省經費. 他需要你幫助他找到最小的經費.

Input

  • 第1行: 一個數:$ N$
  • \(2..N+1\)行: 第\(i+1\)行包含兩個數,分別為第\(i\)塊土地的長和寬

    Output

  • 第一行: 最小的可行費用.

    Sample Input

    4
    100 1
    15 15
    20 5
    1 100

    Sample Output

    500

    輸入解釋:

    共有4塊土地.
    FJ分3組買這些土地:
    第一組:100x1,
    第二組1x100,
    第三組20x5 和 15x15 plot.
    每組的價格分別為100,100,300, 總共500.

顯然有些田是白送的2333.。。。。(只要存在有別的田比這塊田的長和寬都大,那麽這塊田直接白送。。。。。對吧)
然後把這些直接去掉。。。。
由於事先已經知道是斜率優化。。。(提示太明顯了。。感覺刷專題就這一點不怎麽好。。。)

我們就去湊那個式子
先按x從小到大排序。。。因為已經去掉了不用的土地,那麽此時的y一定就是從大到小排的序
\(dp[i]\)表示買前\(i\)塊土地的最優解,所以有了狀態轉移方程方程:
\(dp[i] = dp[j] + x[i] * y[j+1]\)
寫成一次函數的形式:
\(-dp[j]=y[j+1]*x[i]-dp[i]\)
所以每一個點就是\((y[j+1],-dp[j])\)
\(k=x[i]\)滿足單調性
當然這裏就差不多了,但是由於上一篇我說的不是很清楚,我想再多說一句。。。
最開始排除的是兩個點的斜率小於\(k\)的,其實是因為畫圖可以看出後面的點答案一定優於前面的。而當斜率大於k時,雖然當前的答案時前面的點更好(所以計算答案的時候就用的是第一個滿足條件的點),但是由於\(k\)
在單增,有可能\(k\)會超過它的斜率,所以蹦年確定。
而倒著刪是因為如果不是個下凸包形狀的話,那麽中間那個點一定不是最優點,要麽它前面的那個點是最優的,要麽後面的是最優的。


#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4 + 5;
struct lpl{
    long long x, y;
}lin, ini[maxn], field[maxn], point[maxn];
int n, tot, cnt, head, tail;
long long dp[maxn];

inline bool cmp(lpl a, lpl b)
{
    return a.x == b.x ? a.y < b.y : a.x < b.x;
}

inline void putit()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%lld%lld", &ini[i].x, &ini[i].y);
}

inline double slop(lpl a, lpl b)
{
    return (double)(a.y - b.y) / (a.x - b.x);
}

inline void workk()
{
    sort(ini + 1, ini + n + 1, cmp);
    for(int i = 1; i <= n; i++){
        while(tot && ini[i].y >= field[tot].y) tot--;
        field[++tot] = ini[i];
    }
    point[head].x = field[1].y; point[head].y = 0;
    for(int i = 1; i <= tot; ++i){
        while(head < tail && slop(point[head], point[head + 1]) < field[i].x)   head++;
        dp[i] = (-point[head].y) + field[i].x * point[head].x;
        lin.x = field[i + 1].y, lin.y = (-dp[i]);
        while(head < tail && slop(point[tail], point[tail - 1]) > slop(point[tail], lin)) tail--;
        point[++tail] = lin;
    }
    cout << dp[tot];
}

int main()
{
    putit();
    workk();
    return 0;
}

bzoj1597 [Usaco2008 Mar]土地購買