【BZOJ1597】【Usaco2008 Mar】土地购买 斜率优化DP

题目:

题目在这里

思路与做法:

这题如果想要直接dp的话不太好处理。

不过, 我们发现如果$a[i].x>=a[j].x$且$a[i].y>=a[j].y$ $($a是输入的数组,x为长,y为宽$)$, j是没用的, 可以直接dp

容易得出状态转移方程为:$f_i = min \{f_j + x_i * y_{j+1} \}$

可以用斜率优化DP

推导过程:

$f_j + x_i * y_{j+1} < f_k + x_i * y_{k+1}$

$f_j - f_k < x_i*y_{k+1} - x_i*y_{j+1}$

${f_j - _k \over y_{k+1} - y_{j+1}} < x_i$

代码:

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

using namespace std;

const int N = 50010;

struct Data
{   int x, y;
    bool operator < (const Data &rhs) const { return x < rhs.x || (x == rhs.x && y < rhs.y); }
} a[N];
vector<int> x, y;

int Q[N], hd, tl;
long long f[N];
inline double calc(int j, int k) { return (double)(f[j]-f[k]) / (double)(y[k+1]-y[j+1]); }

int main()
{   int n;
    scanf("%d", &n);
    for(int i=1; i<=n; i++)
        scanf("%d %d", &a[i].x, &a[i].y);
    sort(a+1, a+1+n);
    x.push_back(0);
    y.push_back(0);
    for(int i=1; i<=n; i++)
    {   while(x.size() > 1 && y.size() > 1 && y.back() <= a[i].y)
            x.pop_back(), y.pop_back();
        x.push_back(a[i].x);
        y.push_back(a[i].y);
    }
    Q[hd = 0] = 0;
    tl = 1;
    for(int i=1; i<x.size(); i++)
    {   while(hd < tl-1 && calc(Q[hd+1], Q[hd]) <= x[i]) hd++;
        f[i] = f[Q[hd]] + (long long)y[Q[hd]+1] * (long long)x[i];
        while(hd < tl-1 && calc(i, Q[tl-1]) <= calc(Q[tl-1], Q[tl-2])) tl--;
        Q[tl++] = i;
    }
    printf("%lld\n", f[x.size()-1]);
    return 0;
}

发表于 2018-03-18 20:16:56 in 斜率优化DP